AccountS - unifying unitFactor, balanceLimit, costIncrement logic

This commit is contained in:
DanB
2021-01-31 14:53:53 +01:00
parent ab378e7707
commit e64a80eadf
5 changed files with 87 additions and 120 deletions

View File

@@ -41,57 +41,6 @@ type abstractBalance struct {
rateSConns []string
}
// costIncrement finds out the cost increment for the event
func (aB *abstractBalance) costIncrement(tnt string, ev utils.DataProvider) (costIcrm *utils.CostIncrement, err error) {
for _, cIcrm := range aB.blnCfg.CostIncrements {
var pass bool
if pass, err = aB.fltrS.Pass(tnt, cIcrm.FilterIDs, ev); err != nil {
return
} else if !pass {
continue
}
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)
}
return
}
// unitFactor returns the unitFactor for the event
func (aB *abstractBalance) unitFactor(tnt string, ev utils.DataProvider) (uF *utils.UnitFactor, err error) {
for _, uF = range aB.blnCfg.UnitFactors {
var pass bool
if pass, err = aB.fltrS.Pass(tnt, uF.FilterIDs, ev); err != nil {
return
} else if !pass {
continue
}
return
}
return
}
// balanceLimit returns the balance's limit
func (aB *abstractBalance) balanceLimit() (bL *utils.Decimal) {
if _, isUnlimited := aB.blnCfg.Opts[utils.MetaBalanceUnlimited]; isUnlimited {
return
}
if lmtIface, has := aB.blnCfg.Opts[utils.MetaBalanceLimit]; has {
bL = lmtIface.(*utils.Decimal)
}
// nothing matched, return default
bL = utils.NewDecimal(0, 0)
return
}
// debitUsage implements the balanceOperator interface
func (aB *abstractBalance) debitUsage(usage *utils.Decimal,
cgrEv *utils.CGREvent) (dbted *utils.Decimal, ec *utils.EventCharges, err error) {
@@ -111,7 +60,8 @@ func (aB *abstractBalance) debitUsage(usage *utils.Decimal,
// costIncrement
var costIcrm *utils.CostIncrement
if costIcrm, err = aB.costIncrement(cgrEv.Tenant, evNm); err != nil {
if costIcrm, err = costIncrement(aB.blnCfg.CostIncrements, aB.fltrS,
cgrEv.Tenant, evNm); err != nil {
return
}
if costIcrm.RecurrentFee.Cmp(decimal.New(-1, 0)) == 0 &&
@@ -129,7 +79,10 @@ func (aB *abstractBalance) debitUsage(usage *utils.Decimal,
// balanceLimit
var hasLmt bool
blncLmt := aB.balanceLimit()
var blncLmt *utils.Decimal
if blncLmt, err = balanceLimit(aB.blnCfg.Opts); err != nil {
return
}
if blncLmt != nil && blncLmt.Cmp(decimal.New(0, 0)) != 0 {
aB.blnCfg.Units.Big = utils.SubstractBig(aB.blnCfg.Units.Big, blncLmt.Big)
hasLmt = true
@@ -137,7 +90,7 @@ func (aB *abstractBalance) debitUsage(usage *utils.Decimal,
// unitFactor
var uF *utils.UnitFactor
if uF, err = aB.unitFactor(cgrEv.Tenant, evNm); err != nil {
if uF, err = unitFactor(aB.blnCfg.UnitFactors, aB.fltrS, cgrEv.Tenant, evNm); err != nil {
return
}
var hasUF bool
@@ -167,7 +120,7 @@ func (aB *abstractBalance) debitUsage(usage *utils.Decimal,
return nil, nil, utils.ErrMaxIncrementsExceeded
}
qriedUsage := usage.Big // so we can detect loops
if err = debitUsageFromConcrete(aB.cncrtBlncs, usage, costIcrm, cgrEv,
if err = debitUsageFromConcretes(aB.cncrtBlncs, usage, costIcrm, cgrEv,
aB.connMgr, aB.rateSConns, aB.blnCfg.RateProfileIDs); err != nil {
if err != utils.ErrInsufficientCredit {
return

View File

@@ -26,7 +26,7 @@ import (
"github.com/cgrates/cgrates/utils"
)
func TestDebitUsageFromConcrete(t *testing.T) {
func TestdebitUsageFromConcretes(t *testing.T) {
aB := &abstractBalance{
cncrtBlncs: []*concreteBalance{
{
@@ -56,7 +56,7 @@ func TestDebitUsageFromConcrete(t *testing.T) {
},
}}
// consume only from first balance
if err := debitUsageFromConcrete(aB.cncrtBlncs,
if err := debitUsageFromConcretes(aB.cncrtBlncs,
utils.NewDecimal(int64(time.Duration(5*time.Minute)), 0),
&utils.CostIncrement{
Increment: utils.NewDecimal(int64(time.Duration(time.Minute)), 0),
@@ -73,7 +73,7 @@ func TestDebitUsageFromConcrete(t *testing.T) {
aB.cncrtBlncs[0].blnCfg.Units = utils.NewDecimal(500, 0)
aB.cncrtBlncs[1].blnCfg.Units = utils.NewDecimal(125, 2)
if err := debitUsageFromConcrete(aB.cncrtBlncs,
if err := debitUsageFromConcretes(aB.cncrtBlncs,
utils.NewDecimal(int64(time.Duration(9*time.Minute)), 0),
&utils.CostIncrement{
Increment: utils.NewDecimal(int64(time.Duration(time.Minute)), 0),
@@ -90,7 +90,7 @@ func TestDebitUsageFromConcrete(t *testing.T) {
aB.cncrtBlncs[0].blnCfg.Units = utils.NewDecimal(500, 0)
aB.cncrtBlncs[1].blnCfg.Units = utils.NewDecimal(125, 2)
if err := debitUsageFromConcrete(aB.cncrtBlncs,
if err := debitUsageFromConcretes(aB.cncrtBlncs,
utils.NewDecimal(int64(time.Duration(10*time.Minute)), 0),
&utils.CostIncrement{
Increment: utils.NewDecimal(int64(time.Duration(time.Minute)), 0),

View File

@@ -43,58 +43,6 @@ type concreteBalance struct {
rateSConns []string
}
// costIncrement finds out the cost increment for the event
func (cB *concreteBalance) costIncrement(tnt string, ev utils.DataProvider) (costIcrm *utils.CostIncrement, err error) {
for _, cIcrm := range cB.blnCfg.CostIncrements {
var pass bool
if pass, err = cB.fltrS.Pass(tnt, cIcrm.FilterIDs, ev); err != nil {
return
} else if !pass {
continue
}
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)
}
return
}
// unitFactor returns the unitFactor for the event
func (cB *concreteBalance) unitFactor(tnt string, ev utils.DataProvider) (uF *utils.UnitFactor, err error) {
for _, uF = range cB.blnCfg.UnitFactors {
var pass bool
if pass, err = cB.fltrS.Pass(tnt, uF.FilterIDs, ev); err != nil {
return
} else if !pass {
continue
}
return
}
return
}
// balanceLimit returns the balance's limit
func (cB *concreteBalance) balanceLimit() (bL *utils.Decimal) {
if _, isUnlimited := cB.blnCfg.Opts[utils.MetaBalanceUnlimited]; isUnlimited {
return
}
if lmtIface, has := cB.blnCfg.Opts[utils.MetaBalanceLimit]; has {
bL = lmtIface.(*utils.Decimal)
return
}
// nothing matched, return default
bL = utils.NewDecimal(0, 0)
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) {
@@ -109,7 +57,7 @@ func (cB *concreteBalance) debitUnits(dUnts *utils.Decimal, tnt string,
// unitFactor
var hasUF bool
if uF, err = cB.unitFactor(tnt, ev); err != nil {
if uF, err = unitFactor(cB.blnCfg.UnitFactors, cB.fltrS, tnt, ev); err != nil {
return
}
if uF != nil && uF.Factor.Cmp(decimal.New(1, 0)) != 0 {
@@ -119,7 +67,10 @@ func (cB *concreteBalance) debitUnits(dUnts *utils.Decimal, tnt string,
// balanceLimit
var hasLmt bool
blncLmt := cB.balanceLimit()
var blncLmt *utils.Decimal
if blncLmt, err = balanceLimit(cB.blnCfg.Opts); err != nil {
return
}
if blncLmt != nil && blncLmt.Big.Cmp(decimal.New(0, 0)) != 0 {
cB.blnCfg.Units.Big = utils.SubstractBig(cB.blnCfg.Units.Big, blncLmt.Big)
hasLmt = true
@@ -161,7 +112,8 @@ func (cB *concreteBalance) debitUsage(usage *utils.Decimal,
// costIncrement
var costIcrm *utils.CostIncrement
if costIcrm, err = cB.costIncrement(cgrEv.Tenant, evNm); err != nil {
if costIcrm, err = costIncrement(cB.blnCfg.CostIncrements,
cB.fltrS, cgrEv.Tenant, evNm); err != nil {
return
}
if costIcrm.RecurrentFee.Cmp(decimal.New(-1, 0)) == 0 &&
@@ -179,7 +131,10 @@ func (cB *concreteBalance) debitUsage(usage *utils.Decimal,
// balanceLimit
var hasLmt bool
blncLmt := cB.balanceLimit()
var blncLmt *utils.Decimal
if blncLmt, err = balanceLimit(cB.blnCfg.Opts); err != nil {
return
}
if blncLmt != nil && blncLmt.Cmp(decimal.New(0, 0)) != 0 {
cB.blnCfg.Units.Big = utils.SubstractBig(cB.blnCfg.Units.Big, blncLmt.Big)
hasLmt = true
@@ -187,7 +142,7 @@ func (cB *concreteBalance) debitUsage(usage *utils.Decimal,
// unitFactor
var uF *utils.UnitFactor
if uF, err = cB.unitFactor(cgrEv.Tenant, evNm); err != nil {
if uF, err = unitFactor(cB.blnCfg.UnitFactors, cB.fltrS, cgrEv.Tenant, evNm); err != nil {
return
}
var hasUF bool

View File

@@ -34,7 +34,7 @@ func TestCBDebitUnits(t *testing.T) {
ID: "TestCBDebitUnits",
Type: utils.MetaConcrete,
Opts: map[string]interface{}{
utils.MetaBalanceLimit: utils.NewDecimal(-200, 0),
utils.MetaBalanceLimit: -200.0,
},
UnitFactors: []*utils.UnitFactor{
{
@@ -63,7 +63,7 @@ func TestCBDebitUnits(t *testing.T) {
ID: "TestCBDebitUnits",
Type: utils.MetaConcrete,
Opts: map[string]interface{}{
utils.MetaBalanceLimit: utils.NewDecimal(-1, 0),
utils.MetaBalanceLimit: -1.0,
},
Units: utils.NewDecimal(125, 2), // 1.25
},
@@ -107,7 +107,7 @@ func TestCBDebitUnits(t *testing.T) {
ID: "TestCBDebitUnits",
Type: utils.MetaConcrete,
Opts: map[string]interface{}{
utils.MetaBalanceLimit: utils.NewDecimal(5, 1), // 0.5 as limit
utils.MetaBalanceLimit: 0.5, // 0.5 as limit
},
Units: utils.NewDecimal(125, 2), // 1.25
},

View File

@@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package accounts
import (
"errors"
"fmt"
"github.com/cgrates/cgrates/engine"
@@ -138,9 +139,66 @@ func rateSCostForEvent(connMgr *engine.ConnManager, cgrEv *utils.CGREvent,
return
}
// costIncrement computes the costIncrement for the event
func costIncrement(cfgCostIncrmts []*utils.CostIncrement,
fltrS *engine.FilterS, tnt string, ev utils.DataProvider) (costIcrm *utils.CostIncrement, err error) {
for _, cIcrm := range cfgCostIncrmts {
var pass bool
if pass, err = fltrS.Pass(tnt, cIcrm.FilterIDs, ev); err != nil {
return
} else if !pass {
continue
}
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)
}
return
}
// unitFactor detects the unitFactor for the event
func unitFactor(cfgUnitFactors []*utils.UnitFactor,
fltrS *engine.FilterS, tnt string, ev utils.DataProvider) (uF *utils.UnitFactor, err error) {
for _, uF = range cfgUnitFactors {
var pass bool
if pass, err = fltrS.Pass(tnt, uF.FilterIDs, ev); err != nil {
return
} else if !pass {
continue
}
return
}
return
}
// balanceLimit returns the balance limit based on configuration
func balanceLimit(optsCfg map[string]interface{}) (bL *utils.Decimal, err error) {
if _, isUnlimited := optsCfg[utils.MetaBalanceUnlimited]; isUnlimited {
return // unlimited is nil pointer
}
if lmtIface, has := optsCfg[utils.MetaBalanceLimit]; has {
flt64Lmt, canCast := lmtIface.(float64)
if !canCast {
return nil, errors.New("unsupported *balanceLimit format")
}
return utils.NewDecimalFromFloat64(flt64Lmt), nil
}
// nothing matched, return default
bL = utils.NewDecimal(0, 0)
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,
func debitUsageFromConcretes(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 &&
@@ -149,6 +207,7 @@ func debitUsageFromConcrete(cncrtBlncs []*concreteBalance, usage *utils.Decimal,
if rplyCost, err = rateSCostForEvent(connMgr, cgrEv, rateSConns, rpIDs); err != nil {
return
}
costIcrm = costIcrm.Clone() // so we don't modify the original
costIcrm.FixedFee = utils.NewDecimalFromFloat64(rplyCost.Cost)
}
var tCost *decimal.Big