From 7b208bd10cf4035be1155d8156d0263d38abb2f4 Mon Sep 17 00:00:00 2001 From: DanB Date: Fri, 29 Jan 2021 18:38:37 +0100 Subject: [PATCH] AccountS - debitUsage without startTime, standalone processAttributes method --- accounts/abstractbalance.go | 31 ++--------- accounts/abstractbalance_test.go | 20 +++---- accounts/concretebalance.go | 89 ++++++++++++++++++++++++-------- accounts/libaccounts.go | 29 +++++++++-- 4 files changed, 107 insertions(+), 62 deletions(-) diff --git a/accounts/abstractbalance.go b/accounts/abstractbalance.go index 353cb5191..10e75b5d6 100644 --- a/accounts/abstractbalance.go +++ b/accounts/abstractbalance.go @@ -19,8 +19,6 @@ along with this program. If not, see package accounts import ( - "time" - "github.com/cgrates/cgrates/engine" "github.com/cgrates/cgrates/utils" "github.com/ericlagergren/decimal" @@ -94,30 +92,6 @@ func (aB *abstractBalance) balanceLimit() (bL *utils.Decimal) { return } -// processAttributeS will process the event with AttributeS -func (aB *abstractBalance) processAttributeS(cgrEv *utils.CGREvent) (rplyEv *engine.AttrSProcessEventReply, err error) { - if len(aB.attrSConns) == 0 { - return nil, utils.NewErrNotConnected(utils.AttributeS) - } - var procRuns *int - if val, has := cgrEv.Opts[utils.OptsAttributesProcessRuns]; has { - if v, err := utils.IfaceAsTInt64(val); err == nil { - procRuns = utils.IntPointer(int(v)) - } - } - attrArgs := &engine.AttrArgsProcessEvent{ - Context: utils.StringPointer(utils.FirstNonEmpty( - engine.MapEvent(cgrEv.Opts).GetStringIgnoreErrors(utils.OptsContext), - utils.MetaAccountS)), - CGREvent: cgrEv, - AttributeIDs: aB.blnCfg.AttributeIDs, - ProcessRuns: procRuns, - } - err = aB.connMgr.Call(aB.attrSConns, nil, utils.AttributeSv1ProcessEvent, - attrArgs, &rplyEv) - return -} - // rateSCostForEvent will process the event with RateS in order to get the cost func (aB *abstractBalance) rateSCostForEvent(cgrEv *utils.CGREvent) (rplyCost *engine.RateProfileCost, err error) { if len(aB.rateSConns) == 0 { @@ -177,7 +151,7 @@ func (aB *abstractBalance) debitUsageFromConcrete(usage *utils.Decimal, } // debitUsage implements the balanceOperator interface -func (aB *abstractBalance) debitUsage(usage *utils.Decimal, startTime time.Time, +func (aB *abstractBalance) debitUsage(usage *utils.Decimal, cgrEv *utils.CGREvent) (dbted *utils.Decimal, ec *utils.EventCharges, err error) { evNm := utils.MapStorage{ @@ -202,7 +176,8 @@ func (aB *abstractBalance) debitUsage(usage *utils.Decimal, startTime time.Time, costIcrm.FixedFee == nil && len(aB.blnCfg.AttributeIDs) != 0 { // cost unknown, apply AttributeS to query from RateS var rplyAttrS *engine.AttrSProcessEventReply - if rplyAttrS, err = aB.processAttributeS(cgrEv); err != nil { + if rplyAttrS, err = processAttributeS(aB.connMgr, cgrEv, aB.attrSConns, + aB.blnCfg.AttributeIDs); err != nil { return } if len(rplyAttrS.AlteredFields) != 0 { // event was altered diff --git a/accounts/abstractbalance_test.go b/accounts/abstractbalance_test.go index 0e6bd6235..288eafdad 100644 --- a/accounts/abstractbalance_test.go +++ b/accounts/abstractbalance_test.go @@ -133,8 +133,8 @@ func TestABDebitUsage(t *testing.T) { fltrS: new(engine.FilterS), } - if dbted, _, err := aB.debitUsage(utils.NewDecimal(int64(time.Duration(30*time.Second)), 0), - time.Now(), new(utils.CGREvent)); err != nil { + if dbted, _, err := aB.debitUsage(utils.NewDecimal(int64(30*time.Second), 0), + new(utils.CGREvent)); err != nil { t.Error(err) } else if dbted.Compare(utils.NewDecimal(int64(30*time.Second), 0)) != 0 { t.Errorf("Unexpected debited units: %s", dbted) @@ -148,8 +148,8 @@ func TestABDebitUsage(t *testing.T) { aB.blnCfg.Units = utils.NewDecimal(int64(time.Duration(60*time.Second)), 0) aB.cncrtBlncs[0].blnCfg.Units = utils.NewDecimal(29, 0) // not enough concrete - if dbted, _, err := aB.debitUsage(utils.NewDecimal(int64(time.Duration(30*time.Second)), 0), - time.Now(), new(utils.CGREvent)); err != nil { + if dbted, _, err := aB.debitUsage(utils.NewDecimal(int64(30*time.Second), 0), + new(utils.CGREvent)); err != nil { t.Error(err) } else if dbted.Compare(utils.NewDecimal(int64(29*time.Second), 0)) != 0 { t.Errorf("Unexpected debited units: %s", dbted) @@ -162,12 +162,12 @@ func TestABDebitUsage(t *testing.T) { // limited by concrete aB.cncrtBlncs[0].blnCfg.Units = utils.NewDecimal(0, 0) // not enough concrete - if dbted, _, err := aB.debitUsage(utils.NewDecimal(int64(time.Duration(30*time.Second)), 0), - time.Now(), new(utils.CGREvent)); err != nil { + if dbted, _, err := aB.debitUsage(utils.NewDecimal(int64(30*time.Second), 0), + new(utils.CGREvent)); err != nil { t.Error(err) } else if dbted.Compare(utils.NewDecimal(0, 0)) != 0 { t.Errorf("Unexpected debited units: %s", dbted) - } else if aB.blnCfg.Units.Compare(utils.NewDecimal(int64(time.Duration(31*time.Second)), 0)) != 0 { // same as above + } else if aB.blnCfg.Units.Compare(utils.NewDecimal(int64(31*time.Second), 0)) != 0 { // same as above t.Errorf("Unexpected units in abstract balance: %s", aB.blnCfg.Units) } else if aB.cncrtBlncs[0].blnCfg.Units.Compare(utils.NewDecimal(0, 0)) != 0 { // same as above t.Errorf("Unexpected units in concrete balance: %s", aB.cncrtBlncs[0].blnCfg.Units) @@ -177,10 +177,10 @@ func TestABDebitUsage(t *testing.T) { aB.blnCfg.Units = utils.NewDecimal(int64(time.Duration(29*time.Second)), 0) // not enough abstract aB.cncrtBlncs[0].blnCfg.Units = utils.NewDecimal(60, 0) - if dbted, _, err := aB.debitUsage(utils.NewDecimal(int64(time.Duration(30*time.Second)), 0), - time.Now(), new(utils.CGREvent)); err != nil { + if dbted, _, err := aB.debitUsage(utils.NewDecimal(int64(30*time.Second), 0), + new(utils.CGREvent)); err != nil { t.Error(err) - } else if dbted.Compare(utils.NewDecimal(int64(time.Duration(29*time.Second)), 0)) != 0 { + } else if dbted.Compare(utils.NewDecimal(int64(29*time.Second), 0)) != 0 { t.Errorf("Unexpected debited units: %s", dbted) } else if aB.blnCfg.Units.Compare(utils.NewDecimal(0, 0)) != 0 { // should be all used t.Errorf("Unexpected units in abstract balance: %s", aB.blnCfg.Units) diff --git a/accounts/concretebalance.go b/accounts/concretebalance.go index 3573bbb08..0d8fbcea2 100644 --- a/accounts/concretebalance.go +++ b/accounts/concretebalance.go @@ -19,7 +19,7 @@ along with this program. If not, see package accounts import ( - "time" + "fmt" "github.com/cgrates/cgrates/engine" "github.com/cgrates/cgrates/utils" @@ -91,26 +91,6 @@ func (cB *concreteBalance) balanceLimit() (bL *utils.Decimal) { return } -// debit implements the balanceOperator interface -func (cB *concreteBalance) debitUsage(usage *utils.Decimal, startTime time.Time, - cgrEv *utils.CGREvent) (dbted *utils.Decimal, ec *utils.EventCharges, err error) { - - evNm := utils.MapStorage{ - utils.MetaOpts: cgrEv.Opts, - utils.MetaReq: cgrEv.Event, - } - - // pass the general balance filters - var pass bool - if pass, err = cB.fltrS.Pass(cgrEv.Tenant, cB.blnCfg.FilterIDs, evNm); err != nil { - return - } else if !pass { - return nil, nil, utils.ErrFilterNotPassingNoCaps - } - - return -} - // debitUnits is a direct debit of balance units func (cB *concreteBalance) debitUnits(dUnts *utils.Decimal, tnt string, ev utils.DataProvider) (dbted *utils.Decimal, uF *utils.UnitFactor, err error) { @@ -158,6 +138,72 @@ func (cB *concreteBalance) debitUnits(dUnts *utils.Decimal, tnt string, return } +// debit implements the balanceOperator interface +func (cB *concreteBalance) debitUsage(usage *utils.Decimal, + cgrEv *utils.CGREvent) (dbted *utils.Decimal, ec *utils.EventCharges, err error) { + + evNm := utils.MapStorage{ + utils.MetaOpts: cgrEv.Opts, + utils.MetaReq: cgrEv.Event, + } + + // pass the general balance filters + var pass bool + if pass, err = cB.fltrS.Pass(cgrEv.Tenant, cB.blnCfg.FilterIDs, evNm); err != nil { + return + } else if !pass { + return nil, nil, utils.ErrFilterNotPassingNoCaps + } + + // costIncrement + var costIcrm *utils.CostIncrement + if costIcrm, err = cB.costIncrement(cgrEv.Tenant, evNm); err != nil { + return + } + if costIcrm.RecurrentFee.Cmp(decimal.New(-1, 0)) == 0 && + costIcrm.FixedFee == nil && + len(cB.blnCfg.AttributeIDs) != 0 { // cost unknown, apply AttributeS to query from RateS + var rplyAttrS *engine.AttrSProcessEventReply + if rplyAttrS, err = processAttributeS(cB.connMgr, cgrEv, cB.attrSConns, + cB.blnCfg.AttributeIDs); err != nil { + return + } + if len(rplyAttrS.AlteredFields) != 0 { // event was altered + cgrEv = rplyAttrS.CGREvent + } + } + + // balanceLimit + var hasLmt bool + blncLmt := cB.balanceLimit() + if blncLmt != nil && blncLmt.Cmp(decimal.New(0, 0)) != 0 { + cB.blnCfg.Units.Big = utils.SubstractBig(cB.blnCfg.Units.Big, blncLmt.Big) + hasLmt = true + } + + // unitFactor + var uF *utils.UnitFactor + if uF, err = cB.unitFactor(cgrEv.Tenant, evNm); err != nil { + return + } + var hasUF bool + if uF != nil && uF.Factor.Cmp(decimal.New(1, 0)) != 0 { + usage.Big = utils.MultiplyBig(usage.Big, uF.Factor.Big) + hasUF = true + } + + // balance smaller than usage, correct usage + if cB.blnCfg.Units.Compare(usage) == -1 { + // decrease the usage to match the maximum increments + // will use special rounding to 0 since otherwise we go negative (ie: 0.05 as increment) + usage.Big = roundedUsageWithIncrements(cB.blnCfg.Units.Big, costIcrm.Increment.Big) + } + + fmt.Printf("hasLmt: %v, hasUF: %v\n", hasLmt, hasUF) + + return +} + // cloneUnitsFromConcretes returns cloned units from the concrete balances passed as parameters func cloneUnitsFromConcretes(cBs []*concreteBalance) (clnedUnts []*utils.Decimal) { if cBs == nil { @@ -170,6 +216,7 @@ func cloneUnitsFromConcretes(cBs []*concreteBalance) (clnedUnts []*utils.Decimal return } +// restoreUnitsFromClones will restore the units from the clones func restoreUnitsFromClones(cBs []*concreteBalance, clnedUnts []*utils.Decimal) { for i, clnedUnt := range clnedUnts { cBs[i].blnCfg.Units.Big = clnedUnt.Big diff --git a/accounts/libaccounts.go b/accounts/libaccounts.go index 04ce32737..d27c8503a 100644 --- a/accounts/libaccounts.go +++ b/accounts/libaccounts.go @@ -20,7 +20,6 @@ package accounts import ( "fmt" - "time" "github.com/cgrates/cgrates/engine" "github.com/cgrates/cgrates/utils" @@ -91,8 +90,7 @@ func newBalanceOperator(blncCfg *utils.Balance, cncrtBlncs []*concreteBalance, // balanceOperator is the implementation of a balance type type balanceOperator interface { - debitUsage(usage *utils.Decimal, startTime time.Time, - cgrEv *utils.CGREvent) (dbted *utils.Decimal, ec *utils.EventCharges, err error) + debitUsage(usage *utils.Decimal, cgrEv *utils.CGREvent) (dbted *utils.Decimal, ec *utils.EventCharges, err error) } // roundUsageWithIncrements rounds the usage based on increments @@ -103,3 +101,28 @@ func roundedUsageWithIncrements(usage, incrm *decimal.Big) (rndedUsage *decimal. rndedUsage = utils.MultiplyBig(usgMaxIncrm, incrm) return } + +// processAttributeS will process the event with AttributeS +func processAttributeS(connMgr *engine.ConnManager, cgrEv *utils.CGREvent, + attrSConns, attrIDs []string) (rplyEv *engine.AttrSProcessEventReply, err error) { + if len(attrSConns) == 0 { + return nil, utils.NewErrNotConnected(utils.AttributeS) + } + var procRuns *int + if val, has := cgrEv.Opts[utils.OptsAttributesProcessRuns]; has { + if v, err := utils.IfaceAsTInt64(val); err == nil { + procRuns = utils.IntPointer(int(v)) + } + } + attrArgs := &engine.AttrArgsProcessEvent{ + Context: utils.StringPointer(utils.FirstNonEmpty( + engine.MapEvent(cgrEv.Opts).GetStringIgnoreErrors(utils.OptsContext), + utils.MetaAccountS)), + CGREvent: cgrEv, + AttributeIDs: attrIDs, + ProcessRuns: procRuns, + } + err = connMgr.Call(attrSConns, nil, utils.AttributeSv1ProcessEvent, + attrArgs, &rplyEv) + return +}