RateS - return ErrNotFound in case of no profile matched, AccountS - testAccountSv1MaxUsage

This commit is contained in:
DanB
2021-02-08 17:10:46 +01:00
parent 3198ca2ab7
commit 7465f25ba0
10 changed files with 87 additions and 34 deletions

View File

@@ -93,7 +93,6 @@ func (aB *abstractBalance) debitUsage(usage *utils.Decimal,
// will use special rounding to 0 since otherwise we go negative (ie: 0.05 as increment)
usage.Big = roundedUsageWithIncrements(aB.blnCfg.Units.Big, costIcrm.Increment.Big)
}
// attempt to debit usage with cost
if ec, err = maxDebitUsageFromConcretes(aB.cncrtBlncs, usage,
aB.connMgr, cgrEv,

View File

@@ -176,17 +176,20 @@ func (aS *AccountS) accountProcessEvent(acnt *utils.AccountProfile,
} else {
usage.Big = decimal.New(int64(usgEv), 0)
}
for _, blncOper := range blncOpers {
for i, blncOper := range blncOpers {
if i == 0 {
ec = utils.NewEventCharges()
}
if usage.Big.Cmp(decimal.New(0, 0)) == 0 {
return // no more debit
}
var ecDbt *utils.EventCharges
if ec, err = blncOper.debitUsage(usage, cgrEv); err != nil {
if ecDbt, err = blncOper.debitUsage(usage.Clone(), cgrEv); err != nil {
if err == utils.ErrFilterNotPassingNoCaps {
err = nil
continue
}
return
}
usage.Big = utils.SubstractBig(usage.Big, ecDbt.Usage)
ec.Merge(ecDbt)
@@ -209,7 +212,7 @@ func (aS *AccountS) V1AccountProfileForEvent(args *utils.ArgsAccountForEvent, ap
}
// V1MaxUsage returns the maximum usage for the event, based on matching Account
func (aS *AccountS) V1MaxUsage(args *utils.ArgsAccountForEvent, ec *utils.EventCharges) (err error) {
func (aS *AccountS) V1MaxUsage(args *utils.ArgsAccountForEvent, eEc *utils.ExtEventCharges) (err error) {
var acnt *utils.AccountProfile
var lkID string
if acnt, lkID, err = aS.matchingAccountForEvent(args.CGREvent.Tenant,
@@ -225,13 +228,16 @@ func (aS *AccountS) V1MaxUsage(args *utils.ArgsAccountForEvent, ec *utils.EventC
if procEC, err = aS.accountProcessEvent(acnt, args.CGREvent); err != nil {
return
}
*ec = *procEC
var rcvEec *utils.ExtEventCharges
if rcvEec, err = procEC.AsExtEventCharges(); err != nil {
return
}
*eEc = *rcvEec
return
}
// V1DebitUsage performs debit for the provided event
func (aS *AccountS) V1DebitUsage(args *utils.ArgsAccountForEvent, ec *utils.EventCharges) (err error) {
func (aS *AccountS) V1DebitUsage(args *utils.ArgsAccountForEvent, eEc *utils.ExtEventCharges) (err error) {
var acnt *utils.AccountProfile
var lkID string
if acnt, lkID, err = aS.matchingAccountForEvent(args.CGREvent.Tenant,
@@ -248,10 +254,15 @@ func (aS *AccountS) V1DebitUsage(args *utils.ArgsAccountForEvent, ec *utils.Even
return
}
var rcvEec *utils.ExtEventCharges
if rcvEec, err = procEC.AsExtEventCharges(); err != nil {
return
}
if err = aS.dm.SetAccountProfile(acnt, false); err != nil {
return // no need of revert since we did not save
}
*ec = *procEC
*eEc = *rcvEec
return
}

View File

@@ -112,7 +112,6 @@ func (cB *concreteBalance) debitUnits(dUnts *utils.Decimal, tnt string,
// debit implements the balanceOperator interface
func (cB *concreteBalance) debitUsage(usage *utils.Decimal,
cgrEv *utils.CGREvent) (ec *utils.EventCharges, err error) {
evNm := utils.MapStorage{
utils.MetaOpts: cgrEv.Opts,
utils.MetaReq: cgrEv.Event,

View File

@@ -40,13 +40,19 @@ func newAccountBalanceOperators(acnt *utils.AccountProfile,
}
blnCfgs.Sort()
var cncrtBlncs []*concreteBalance
blncOpers = make([]balanceOperator, len(blnCfgs))
for i, blnCfg := range blnCfgs {
var cncrtBlncs []*concreteBalance
for i, blnCfg := range blnCfgs { // build the concrete balances
if blnCfg.Type != utils.MetaConcrete {
continue
}
blncOpers[i] = newConcreteBalanceOperator(blnCfg,
fltrS, connMgr, attrSConns, rateSConns)
cncrtBlncs = append(cncrtBlncs, blncOpers[i].(*concreteBalance))
}
for i, blnCfg := range blnCfgs { // build the abstract balances
if blnCfg.Type == utils.MetaConcrete {
blncOpers[i] = newConcreteBalanceOperator(blnCfg,
fltrS, connMgr, attrSConns, rateSConns)
cncrtBlncs = append(cncrtBlncs, blncOpers[i].(*concreteBalance))
continue
}
if blncOpers[i], err = newBalanceOperator(blnCfg, cncrtBlncs, fltrS, connMgr,
@@ -189,6 +195,7 @@ func debitUsageFromConcretes(cncrtBlncs []*concreteBalance, usage *utils.Decimal
costIcrm.FixedFee == nil {
var rplyCost *engine.RateProfileCost
if rplyCost, err = rateSCostForEvent(connMgr, cgrEv, rateSConns, rpIDs); err != nil {
err = utils.NewErrRateS(err)
return
}
costIcrm = costIcrm.Clone() // so we don't modify the original
@@ -235,7 +242,6 @@ func maxDebitUsageFromConcretes(cncrtBlncs []*concreteBalance, usage *utils.Deci
connMgr *engine.ConnManager, cgrEv *utils.CGREvent,
attrSConns, attributeIDs, rateSConns, rpIDs []string,
costIcrm *utils.CostIncrement) (ec *utils.EventCharges, err error) {
// process AttributeS if needed
if costIcrm.RecurrentFee.Cmp(decimal.New(-1, 0)) == 0 &&
costIcrm.FixedFee == nil &&

View File

@@ -166,12 +166,12 @@ func (aSv1 *AccountSv1) AccountProfileForEvent(args *utils.ArgsAccountForEvent,
// MaxUsage returns the maximum usage for the event, based on matching Account
func (aSv1 *AccountSv1) MaxUsage(args *utils.ArgsAccountForEvent,
ec *utils.EventCharges) (err error) {
return aSv1.aS.V1MaxUsage(args, ec)
eEc *utils.ExtEventCharges) (err error) {
return aSv1.aS.V1MaxUsage(args, eEc)
}
// DebitUsage performs debit for the provided event
func (aSv1 *AccountSv1) DebitUsage(args *utils.ArgsAccountForEvent,
ec *utils.EventCharges) (err error) {
return aSv1.aS.V1DebitUsage(args, ec)
eEc *utils.ExtEventCharges) (err error) {
return aSv1.aS.V1DebitUsage(args, eEc)
}

View File

@@ -51,6 +51,7 @@ func TestAccountSv1IT(t *testing.T) {
testAccountSv1RPCConn,
testAccountSv1LoadFromFolder,
testAccountSv1AccountProfileForEvent,
testAccountSv1MaxUsage,
}
switch *dbType {
case utils.MetaInternal:
@@ -144,13 +145,9 @@ func testAccountSv1AccountProfileForEvent(t *testing.T) {
AttributeIDs: []string{},
RateProfileIDs: []string{},
UnitFactors: []*utils.UnitFactor{
&utils.UnitFactor{
FilterIDs: []string{"*string:~*req.ToR:*voice"},
Factor: &utils.Decimal{decimal.New(int64(time.Second), 0)},
},
&utils.UnitFactor{
FilterIDs: []string{"*string:~*req.ToR:*data"},
Factor: &utils.Decimal{decimal.New(int64(1024*time.Second), 0)},
Factor: &utils.Decimal{decimal.New(1024, 3)},
},
},
Units: &utils.Decimal{decimal.New(int64(time.Hour), 0)},
@@ -180,11 +177,18 @@ func testAccountSv1AccountProfileForEvent(t *testing.T) {
Units: &utils.Decimal{decimal.New(5, 0)},
},
"MonetaryBalance2": &utils.Balance{
ID: "MonetaryBalance2",
FilterIDs: []string{},
Weight: 10,
Type: utils.MetaConcrete,
CostIncrements: []*utils.CostIncrement{},
ID: "MonetaryBalance2",
FilterIDs: []string{},
Weight: 10,
Type: utils.MetaConcrete,
CostIncrements: []*utils.CostIncrement{
&utils.CostIncrement{
FilterIDs: []string{"*string:~*req.ToR:*voice"},
Increment: &utils.Decimal{decimal.New(int64(time.Second), 0)},
FixedFee: &utils.Decimal{decimal.New(0, 0)},
RecurrentFee: &utils.Decimal{decimal.New(1, 0)},
},
},
AttributeIDs: []string{},
RateProfileIDs: []string{},
UnitFactors: []*utils.UnitFactor{},
@@ -206,3 +210,20 @@ func testAccountSv1AccountProfileForEvent(t *testing.T) {
t.Errorf("Expecting : %s \n received: %s", utils.ToJSON(eAcnt), utils.ToJSON(acnt))
}
}
func testAccountSv1MaxUsage(t *testing.T) {
var eEc *utils.ExtEventCharges
if err := acntSRPC.Call(utils.AccountSv1MaxUsage,
&utils.ArgsAccountForEvent{CGREvent: &utils.CGREvent{
Tenant: "cgrates.org",
ID: "testAccountSv1MaxUsage",
Event: map[string]interface{}{
utils.AccountField: "1001",
utils.ToR: utils.MetaVoice,
utils.Usage: "15m",
}}}, &eEc); err != nil {
t.Error(err)
} else if eEc.Usage == nil || *eEc.Usage != 800000000000.0 { // 500s from first monetary + 300s from last monetary
t.Errorf("received usage: %v", *eEc.Usage)
}
}

View File

@@ -1,5 +1,5 @@
#Tenant,ID,FilterIDs,ActivationInterval,Weight,BalanceID,BalanceFilterIDs,BalanceWeight,BalanceBlocker,BalanceType,BalanceOpts,BalanceCostIncrements,BalanceAttributeIDs,BalanceRateProfileIDs,BalanceUnitFactors,BalanceUnits,ThresholdIDs
cgrates.org,1001,*string:~*req.Account:1001,,,MonetaryBalance1,,30,,*concrete,,*string:~*req.ToR:*voice;1000000000;0;0.01;*string:~*req.ToR:*data;1024;0;0.01,,,,5,*none
cgrates.org,1001,,,,GenericBalance1,,20,,*abstract,,*string:~*req.ToR:*voice;1000000000;0;0.01;*string:~*req.ToR:*data;1024;0;0.01,,,*string:~*req.ToR:*voice;1000000000;*string:~*req.ToR:*data;1024000000000,3600000000000,
cgrates.org,1001,,,,MonetaryBalance2,,10,,*concrete,,,,,,3,
cgrates.org,1001,,,,GenericBalance1,,20,,*abstract,,*string:~*req.ToR:*voice;1000000000;0;0.01;*string:~*req.ToR:*data;1024;0;0.01,,,*string:~*req.ToR:*data;1.024,3600000000000,
cgrates.org,1001,,,,MonetaryBalance2,,10,,*concrete,,*string:~*req.ToR:*voice;1000000000;0;1,,,,3,
cgrates.org,1002,*string:~*req.Account:1002,,10,MonetaryBalance1,,,,*concrete,,*string:~*req.ToR:*voice;1000000000;0;0.01;;1;0;1,,,,10,*none
1 #Tenant ID FilterIDs ActivationInterval Weight BalanceID BalanceFilterIDs BalanceWeight BalanceBlocker BalanceType BalanceOpts BalanceCostIncrements BalanceAttributeIDs BalanceRateProfileIDs BalanceUnitFactors BalanceUnits ThresholdIDs
2 cgrates.org 1001 *string:~*req.Account:1001 MonetaryBalance1 30 *concrete *string:~*req.ToR:*voice;1000000000;0;0.01;*string:~*req.ToR:*data;1024;0;0.01 5 *none
3 cgrates.org 1001 GenericBalance1 20 *abstract *string:~*req.ToR:*voice;1000000000;0;0.01;*string:~*req.ToR:*data;1024;0;0.01 *string:~*req.ToR:*voice;1000000000;*string:~*req.ToR:*data;1024000000000 *string:~*req.ToR:*data;1.024 3600000000000
4 cgrates.org 1001 MonetaryBalance2 10 *concrete *string:~*req.ToR:*voice;1000000000;0;1 3
5 cgrates.org 1002 *string:~*req.Account:1002 10 MonetaryBalance1 *concrete *string:~*req.ToR:*voice;1000000000;0;0.01;;1;0;1 10 *none

View File

@@ -198,10 +198,16 @@ func (rS *RateS) V1CostForEvent(args *utils.ArgsCostForEvent, rpCost *engine.Rat
}
var rtPrl *engine.RateProfile
if rtPrl, err = rS.matchingRateProfileForEvent(args.Tenant, rPfIDs, args); err != nil {
return utils.NewErrServerError(err)
if err != utils.ErrNotFound {
err = utils.NewErrServerError(err)
}
return
}
if rcvCost, errCost := rS.rateProfileCostForEvent(rtPrl, args, rS.cfg.RateSCfg().Verbosity); errCost != nil {
return utils.NewErrServerError(errCost)
if errCost != utils.ErrNotFound {
errCost = utils.NewErrServerError(err)
}
return errCost
} else {
*rpCost = *rcvCost
}

View File

@@ -65,6 +65,7 @@ var (
ErrNotConnected = errors.New("NOT_CONNECTED")
RalsErrorPrfx = "RALS_ERROR"
DispatcherErrorPrefix = "DISPATCHER_ERROR"
RateSErrPrfx = "RATES_ERROR"
ErrUnsupportedFormat = errors.New("UNSUPPORTED_FORMAT")
ErrNoDatabaseConn = errors.New("NO_DATA_BASE_CONNECTION")
ErrMaxIncrementsExceeded = errors.New("MAX_INCREMENTS_EXCEEDED")
@@ -204,6 +205,10 @@ func NewErrDispatcherS(err error) error {
return fmt.Errorf("%s:%s", DispatcherErrorPrefix, err.Error())
}
func NewErrRateS(err error) error {
return fmt.Errorf("%s:%s", RateSErrPrfx, err.Error())
}
// Centralized returns for APIs
func APIErrorHandler(errIn error) (err error) {
cgrErr, ok := errIn.(*CGRError)

View File

@@ -24,6 +24,12 @@ import (
"github.com/ericlagergren/decimal"
)
// NewEventChargers instantiates the EventChargers in a central place
func NewEventCharges() (ec *EventCharges) {
ec = new(EventCharges)
return
}
// EventCharges records the charges applied to an Event
type EventCharges struct {
Usage *decimal.Big