From ab378e77076dac519a5a42c5a5cbcbfc53731d7f Mon Sep 17 00:00:00 2001 From: DanB Date: Fri, 29 Jan 2021 20:22:03 +0100 Subject: [PATCH] AccountS - debitUsageFromConcrete --- accounts/abstractbalance.go | 61 +------------------------------ accounts/abstractbalance_test.go | 14 +++---- accounts/concretebalance.go | 18 +++++---- accounts/libaccounts.go | 60 ++++++++++++++++++++++++++++++ sessions/sessionscover_it_test.go | 2 + 5 files changed, 82 insertions(+), 73 deletions(-) diff --git a/accounts/abstractbalance.go b/accounts/abstractbalance.go index 10e75b5d6..70689f21c 100644 --- a/accounts/abstractbalance.go +++ b/accounts/abstractbalance.go @@ -92,64 +92,6 @@ func (aB *abstractBalance) balanceLimit() (bL *utils.Decimal) { 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 { - return nil, utils.NewErrNotConnected(utils.RateS) - } - err = aB.connMgr.Call(aB.rateSConns, nil, utils.RateSv1CostForEvent, - &utils.ArgsCostForEvent{CGREvent: cgrEv}, &rplyCost) - return -} - -// debitUsageFromConcrete attempts to debit the usage out of concrete balances -// returns utils.ErrInsufficientCredit if complete usage cannot be debitted -func (aB *abstractBalance) debitUsageFromConcrete(usage *utils.Decimal, - costIcrm *utils.CostIncrement, cgrEv *utils.CGREvent) (err error) { - if costIcrm.RecurrentFee.Cmp(decimal.New(-1, 0)) == 0 && - costIcrm.FixedFee == nil { - var rplyCost *engine.RateProfileCost - if rplyCost, err = aB.rateSCostForEvent(cgrEv); err != nil { - return - } - costIcrm.FixedFee = utils.NewDecimalFromFloat64(rplyCost.Cost) - } - var tCost *decimal.Big - if costIcrm.FixedFee != nil { - tCost = costIcrm.FixedFee.Big - } - // RecurrentFee is configured, used it with increments - if costIcrm.RecurrentFee.Big.Cmp(decimal.New(-1, 0)) != 0 { - rcrntCost := utils.MultiplyBig( - utils.DivideBig(usage.Big, costIcrm.Increment.Big), - costIcrm.RecurrentFee.Big) - if tCost == nil { - tCost = rcrntCost - } else { - tCost = utils.SumBig(tCost, rcrntCost) - } - } - clnedUnts := cloneUnitsFromConcretes(aB.cncrtBlncs) - for _, cB := range aB.cncrtBlncs { - ev := utils.MapStorage{ - utils.MetaOpts: cgrEv.Opts, - utils.MetaReq: cgrEv.Event, - } - var dbted *utils.Decimal - if dbted, _, err = cB.debitUnits(&utils.Decimal{tCost}, cgrEv.Tenant, ev); err != nil { - restoreUnitsFromClones(aB.cncrtBlncs, clnedUnts) - return - } - tCost = utils.SubstractBig(tCost, dbted.Big) - if tCost.Cmp(decimal.New(0, 0)) <= 0 { - return // have debited all, total is smaller or equal to 0 - } - } - // we could not debit all, put back what we have debited - restoreUnitsFromClones(aB.cncrtBlncs, clnedUnts) - return utils.ErrInsufficientCredit -} - // debitUsage implements the balanceOperator interface func (aB *abstractBalance) debitUsage(usage *utils.Decimal, cgrEv *utils.CGREvent) (dbted *utils.Decimal, ec *utils.EventCharges, err error) { @@ -225,7 +167,8 @@ func (aB *abstractBalance) debitUsage(usage *utils.Decimal, return nil, nil, utils.ErrMaxIncrementsExceeded } qriedUsage := usage.Big // so we can detect loops - if err = aB.debitUsageFromConcrete(usage, costIcrm, cgrEv); err != nil { + if err = debitUsageFromConcrete(aB.cncrtBlncs, usage, costIcrm, cgrEv, + aB.connMgr, aB.rateSConns, aB.blnCfg.RateProfileIDs); err != nil { if err != utils.ErrInsufficientCredit { return } diff --git a/accounts/abstractbalance_test.go b/accounts/abstractbalance_test.go index 288eafdad..f5b323793 100644 --- a/accounts/abstractbalance_test.go +++ b/accounts/abstractbalance_test.go @@ -26,7 +26,7 @@ import ( "github.com/cgrates/cgrates/utils" ) -func TestABDebitUsageFromConcrete(t *testing.T) { +func TestDebitUsageFromConcrete(t *testing.T) { aB := &abstractBalance{ cncrtBlncs: []*concreteBalance{ { @@ -56,12 +56,12 @@ func TestABDebitUsageFromConcrete(t *testing.T) { }, }} // consume only from first balance - if err := aB.debitUsageFromConcrete( + if err := debitUsageFromConcrete(aB.cncrtBlncs, utils.NewDecimal(int64(time.Duration(5*time.Minute)), 0), &utils.CostIncrement{ Increment: utils.NewDecimal(int64(time.Duration(time.Minute)), 0), RecurrentFee: utils.NewDecimal(1, 0)}, - new(utils.CGREvent)); err != nil { + new(utils.CGREvent), nil, nil, nil); err != nil { t.Error(err) } else if aB.cncrtBlncs[0].blnCfg.Units.Compare(utils.NewDecimal(0, 0)) != 0 { t.Errorf("Unexpected units in first balance: %s", aB.cncrtBlncs[0].blnCfg.Units) @@ -73,12 +73,12 @@ func TestABDebitUsageFromConcrete(t *testing.T) { aB.cncrtBlncs[0].blnCfg.Units = utils.NewDecimal(500, 0) aB.cncrtBlncs[1].blnCfg.Units = utils.NewDecimal(125, 2) - if err := aB.debitUsageFromConcrete( + if err := debitUsageFromConcrete(aB.cncrtBlncs, utils.NewDecimal(int64(time.Duration(9*time.Minute)), 0), &utils.CostIncrement{ Increment: utils.NewDecimal(int64(time.Duration(time.Minute)), 0), RecurrentFee: utils.NewDecimal(1, 0)}, - new(utils.CGREvent)); err != nil { + new(utils.CGREvent), nil, nil, nil); err != nil { t.Error(err) } else if aB.cncrtBlncs[0].blnCfg.Units.Compare(utils.NewDecimal(-200, 0)) != 0 { t.Errorf("Unexpected units in first balance: %s", aB.cncrtBlncs[0].blnCfg.Units) @@ -90,12 +90,12 @@ func TestABDebitUsageFromConcrete(t *testing.T) { aB.cncrtBlncs[0].blnCfg.Units = utils.NewDecimal(500, 0) aB.cncrtBlncs[1].blnCfg.Units = utils.NewDecimal(125, 2) - if err := aB.debitUsageFromConcrete( + if err := debitUsageFromConcrete(aB.cncrtBlncs, utils.NewDecimal(int64(time.Duration(10*time.Minute)), 0), &utils.CostIncrement{ Increment: utils.NewDecimal(int64(time.Duration(time.Minute)), 0), RecurrentFee: utils.NewDecimal(1, 0)}, - new(utils.CGREvent)); err == nil || err != utils.ErrInsufficientCredit { + new(utils.CGREvent), nil, nil, nil); err == nil || err != utils.ErrInsufficientCredit { t.Error(err) } else if aB.cncrtBlncs[0].blnCfg.Units.Compare(utils.NewDecimal(500, 0)) != 0 { t.Errorf("Unexpected units in first balance: %s", aB.cncrtBlncs[0].blnCfg.Units) diff --git a/accounts/concretebalance.go b/accounts/concretebalance.go index 0d8fbcea2..6cd822bd4 100644 --- a/accounts/concretebalance.go +++ b/accounts/concretebalance.go @@ -52,14 +52,18 @@ func (cB *concreteBalance) costIncrement(tnt string, ev utils.DataProvider) (cos } else if !pass { continue } - costIcrm = cIcrm.Clone() // need clone since we might modify - return + costIcrm = cIcrm + break + } + if costIcrm == nil { + costIcrm = new(utils.CostIncrement) + } + if costIcrm.Increment == nil { + costIcrm.Increment = utils.NewDecimal(1, 0) + } + if costIcrm.RecurrentFee == nil { + costIcrm.RecurrentFee = utils.NewDecimal(-1, 0) } - // nothing matched, return default - costIcrm = &utils.CostIncrement{ - Increment: &utils.Decimal{decimal.New(1, 0)}, - RecurrentFee: &utils.Decimal{decimal.New(-1, 0)}} - return } diff --git a/accounts/libaccounts.go b/accounts/libaccounts.go index d27c8503a..a1dc0783e 100644 --- a/accounts/libaccounts.go +++ b/accounts/libaccounts.go @@ -126,3 +126,63 @@ func processAttributeS(connMgr *engine.ConnManager, cgrEv *utils.CGREvent, attrArgs, &rplyEv) return } + +// rateSCostForEvent will process the event with RateS in order to get the cost +func rateSCostForEvent(connMgr *engine.ConnManager, cgrEv *utils.CGREvent, + rateSConns, rpIDs []string) (rplyCost *engine.RateProfileCost, err error) { + if len(rateSConns) == 0 { + return nil, utils.NewErrNotConnected(utils.RateS) + } + err = connMgr.Call(rateSConns, nil, utils.RateSv1CostForEvent, + &utils.ArgsCostForEvent{CGREvent: cgrEv, RateProfileIDs: rpIDs}, &rplyCost) + return +} + +// debitUsageFromConcrete attempts to debit the usage out of concrete balances +// returns utils.ErrInsufficientCredit if complete usage cannot be debitted +func debitUsageFromConcrete(cncrtBlncs []*concreteBalance, usage *utils.Decimal, + costIcrm *utils.CostIncrement, cgrEv *utils.CGREvent, + connMgr *engine.ConnManager, rateSConns, rpIDs []string) (err error) { + if costIcrm.RecurrentFee.Cmp(decimal.New(-1, 0)) == 0 && + costIcrm.FixedFee == nil { + var rplyCost *engine.RateProfileCost + if rplyCost, err = rateSCostForEvent(connMgr, cgrEv, rateSConns, rpIDs); err != nil { + return + } + costIcrm.FixedFee = utils.NewDecimalFromFloat64(rplyCost.Cost) + } + var tCost *decimal.Big + if costIcrm.FixedFee != nil { + tCost = costIcrm.FixedFee.Big + } + // RecurrentFee is configured, used it with increments + if costIcrm.RecurrentFee.Big.Cmp(decimal.New(-1, 0)) != 0 { + rcrntCost := utils.MultiplyBig( + utils.DivideBig(usage.Big, costIcrm.Increment.Big), + costIcrm.RecurrentFee.Big) + if tCost == nil { + tCost = rcrntCost + } else { + tCost = utils.SumBig(tCost, rcrntCost) + } + } + clnedUnts := cloneUnitsFromConcretes(cncrtBlncs) + for _, cB := range cncrtBlncs { + ev := utils.MapStorage{ + utils.MetaOpts: cgrEv.Opts, + utils.MetaReq: cgrEv.Event, + } + var dbted *utils.Decimal + if dbted, _, err = cB.debitUnits(&utils.Decimal{tCost}, cgrEv.Tenant, ev); err != nil { + restoreUnitsFromClones(cncrtBlncs, clnedUnts) + return + } + tCost = utils.SubstractBig(tCost, dbted.Big) + if tCost.Cmp(decimal.New(0, 0)) <= 0 { + return // have debited all, total is smaller or equal to 0 + } + } + // we could not debit all, put back what we have debited + restoreUnitsFromClones(cncrtBlncs, clnedUnts) + return utils.ErrInsufficientCredit +} diff --git a/sessions/sessionscover_it_test.go b/sessions/sessionscover_it_test.go index 253ff422c..3969f54a1 100644 --- a/sessions/sessionscover_it_test.go +++ b/sessions/sessionscover_it_test.go @@ -1,3 +1,5 @@ +// +build integration + /* Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments Copyright (C) ITsysCOM GmbH