EventCharges population for zero cost abstract debits

This commit is contained in:
DanB
2021-03-12 17:11:01 +01:00
parent 4b2264f9fb
commit e96f128b1e
11 changed files with 176 additions and 108 deletions

View File

@@ -25,14 +25,16 @@ import (
)
// newAbstractBalance constructs an abstractBalanceOperator
func newAbstractBalanceOperator(blnCfg *utils.Balance, cncrtBlncs []*concreteBalance,
func newAbstractBalanceOperator(acntID string, blnCfg *utils.Balance,
cncrtBlncs []*concreteBalance,
fltrS *engine.FilterS, connMgr *engine.ConnManager,
attrSConns, rateSConns []string) balanceOperator {
return &abstractBalance{blnCfg, cncrtBlncs, fltrS, connMgr, attrSConns, rateSConns}
return &abstractBalance{acntID, blnCfg, cncrtBlncs, fltrS, connMgr, attrSConns, rateSConns}
}
// abstractBalance is the operator for *abstract balance type
type abstractBalance struct {
acntID string
blnCfg *utils.Balance
cncrtBlncs []*concreteBalance // paying balances
fltrS *engine.FilterS
@@ -96,7 +98,56 @@ func (aB *abstractBalance) debitAbstracts(usage *decimal.Big,
(costIcrm.FixedFee == nil ||
costIcrm.FixedFee.Cmp(decimal.New(0, 0)) == 0) {
// cost 0, no need of concrete
ec = &utils.EventCharges{Abstracts: &utils.Decimal{usage}}
ec = utils.NewEventCharges()
ec.Abstracts = &utils.Decimal{usage}
// UnitFactors
var ufID string
if hasUF {
ufID = utils.UUIDSha1Prefix()
ec.UnitFactors[ufID] = uF
}
// Rating
ratingID := utils.UUIDSha1Prefix()
ec.Rating[ratingID] = &utils.RateSInterval{
IntervalStart: utils.NewDecimal(0, 0),
Increments: []*utils.RateSIncrement{
{
IncrementStart: utils.NewDecimal(0, 0),
Rate: &utils.Rate{
ID: utils.MetaCostIncrement,
IntervalRates: []*utils.IntervalRate{
{
FixedFee: utils.NewDecimal(0, 0),
},
},
},
CompressFactor: 1,
Usage: &utils.Decimal{usage},
},
},
CompressFactor: 1,
}
acntID := utils.UUIDSha1Prefix()
ec.Accounting[acntID] = &utils.AccountCharge{
AccountID: aB.acntID,
BalanceID: aB.blnCfg.ID,
Units: &utils.Decimal{usage},
BalanceLimit: blncLmt,
UnitFactorID: ufID,
RatingID: ratingID,
}
ec.ChargingIntervals = []*utils.ChargingInterval{
{
Increments: []*utils.ChargingIncrement{
{
Units: &utils.Decimal{usage},
AccountChargeID: acntID,
CompressFactor: 1,
},
},
CompressFactor: 1,
},
}
} else {
// attempt to debit usage with cost
if ec, err = maxDebitAbstractsFromConcretes(aB.cncrtBlncs, usage,

View File

@@ -59,7 +59,10 @@ func TestABDebitUsageFromConcretes(t *testing.T) {
},
}}
expectedEvCharg := &utils.EventCharges{
Concretes: utils.NewDecimal(5, 0),
Concretes: utils.NewDecimal(5, 0),
Accounting: make(map[string]*utils.AccountCharge),
UnitFactors: make(map[string]*utils.UnitFactor),
Rating: make(map[string]*utils.RateSInterval),
}
// consume only from first balance
if evCh, err := debitAbstractsFromConcretes(aB.cncrtBlncs,
@@ -81,7 +84,10 @@ func TestABDebitUsageFromConcretes(t *testing.T) {
aB.cncrtBlncs[0].blnCfg.Units = utils.NewDecimal(500, 0)
aB.cncrtBlncs[1].blnCfg.Units = utils.NewDecimal(125, 2)
expectedEvCharg = &utils.EventCharges{
Concretes: utils.NewDecimal(9, 0),
Concretes: utils.NewDecimal(9, 0),
Accounting: make(map[string]*utils.AccountCharge),
UnitFactors: make(map[string]*utils.UnitFactor),
Rating: make(map[string]*utils.RateSInterval),
}
if evCh, err := debitAbstractsFromConcretes(aB.cncrtBlncs,

View File

@@ -138,59 +138,6 @@ func (aS *AccountS) matchingAccountsForEvent(tnt string, cgrEv *utils.CGREvent,
return
}
// accountDebit will debit the usage out of an Account
func (aS *AccountS) accountDebit(acnt *utils.AccountProfile, usage *decimal.Big,
cgrEv *utils.CGREvent, concretes bool) (ec *utils.EventCharges, err error) {
// Find balances matching event
blcsWithWeight := make(utils.BalancesWithWeight, 0, len(acnt.Balances))
for _, blnCfg := range acnt.Balances {
var weight float64
if weight, err = engine.WeightFromDynamics(blnCfg.Weights,
aS.fltrS, cgrEv.Tenant, cgrEv.AsDataProvider()); err != nil {
return
}
blcsWithWeight = append(blcsWithWeight, &utils.BalanceWithWeight{blnCfg, weight})
}
blcsWithWeight.Sort()
var blncOpers []balanceOperator
if blncOpers, err = newBalanceOperators(blcsWithWeight.Balances(), aS.fltrS, aS.connMgr,
aS.cfg.AccountSCfg().AttributeSConns, aS.cfg.AccountSCfg().RateSConns); err != nil {
return
}
for i, blncOper := range blncOpers {
debFunc := blncOper.debitAbstracts
if concretes {
debFunc = blncOper.debitConcretes
}
if i == 0 {
ec = utils.NewEventCharges()
}
if usage.Cmp(decimal.New(0, 0)) == 0 {
return // no more debit
}
var ecDbt *utils.EventCharges
if ecDbt, err = debFunc(new(decimal.Big).Copy(usage), cgrEv); err != nil {
if err == utils.ErrFilterNotPassingNoCaps ||
err == utils.ErrNotImplemented {
err = nil
continue
}
return
}
var used *decimal.Big
if concretes {
used = ecDbt.Concretes.Big
} else {
used = ecDbt.Abstracts.Big
}
usage = utils.SubstractBig(usage, used)
ec.Merge(ecDbt)
}
return
}
// accountsDebit will debit an usage out of multiple accounts
func (aS *AccountS) accountsDebit(acnts []*utils.AccountProfileWithWeight,
cgrEv *utils.CGREvent, concretes, store bool) (ec *utils.EventCharges, err error) {
@@ -212,11 +159,9 @@ func (aS *AccountS) accountsDebit(acnts []*utils.AccountProfileWithWeight,
} else {
usage = decimal.New(int64(usgEv), 0)
}
ec = utils.NewEventCharges()
acntBkps := make([]utils.AccountBalancesBackup, len(acnts))
for i, acnt := range acnts {
if i == 0 {
ec = utils.NewEventCharges()
}
if usage.Cmp(decimal.New(0, 0)) == 0 {
return // no more debits
}
@@ -247,6 +192,56 @@ func (aS *AccountS) accountsDebit(acnts []*utils.AccountProfileWithWeight,
return
}
// accountDebit will debit the usage out of an Account
func (aS *AccountS) accountDebit(acnt *utils.AccountProfile, usage *decimal.Big,
cgrEv *utils.CGREvent, concretes bool) (ec *utils.EventCharges, err error) {
// Find balances matching event
blcsWithWeight := make(utils.BalancesWithWeight, 0, len(acnt.Balances))
for _, blnCfg := range acnt.Balances {
var weight float64
if weight, err = engine.WeightFromDynamics(blnCfg.Weights,
aS.fltrS, cgrEv.Tenant, cgrEv.AsDataProvider()); err != nil {
return
}
blcsWithWeight = append(blcsWithWeight, &utils.BalanceWithWeight{blnCfg, weight})
}
blcsWithWeight.Sort()
var blncOpers []balanceOperator
if blncOpers, err = newBalanceOperators(acnt.ID, blcsWithWeight.Balances(), aS.fltrS, aS.connMgr,
aS.cfg.AccountSCfg().AttributeSConns, aS.cfg.AccountSCfg().RateSConns); err != nil {
return
}
ec = utils.NewEventCharges()
for _, blncOper := range blncOpers {
debFunc := blncOper.debitAbstracts
if concretes {
debFunc = blncOper.debitConcretes
}
if usage.Cmp(decimal.New(0, 0)) == 0 {
return // no more debit
}
var ecDbt *utils.EventCharges
if ecDbt, err = debFunc(new(decimal.Big).Copy(usage), cgrEv); err != nil {
if err == utils.ErrFilterNotPassingNoCaps ||
err == utils.ErrNotImplemented {
err = nil
continue
}
return
}
var used *decimal.Big
if concretes {
used = ecDbt.Concretes.Big
} else {
used = ecDbt.Abstracts.Big
}
usage = utils.SubstractBig(usage, used)
ec.Merge(ecDbt)
}
return
}
// V1AccountProfilesForEvent returns the matching AccountProfiles for Event
func (aS *AccountS) V1AccountProfilesForEvent(args *utils.ArgsAccountsForEvent, aps *[]*utils.AccountProfile) (err error) {
var acnts utils.AccountProfilesWithWeight

View File

@@ -44,14 +44,15 @@ func restoreUnitsFromClones(cBs []*concreteBalance, clnedUnts []*utils.Decimal)
}
// newConcreteBalance constructs a concreteBalanceOperator
func newConcreteBalanceOperator(blnCfg *utils.Balance,
func newConcreteBalanceOperator(acntID string, blnCfg *utils.Balance,
fltrS *engine.FilterS, connMgr *engine.ConnManager,
attrSConns, rateSConns []string) balanceOperator {
return &concreteBalance{blnCfg, fltrS, connMgr, attrSConns, rateSConns}
return &concreteBalance{acntID, blnCfg, fltrS, connMgr, attrSConns, rateSConns}
}
// concreteBalance is the operator for *concrete balance type
type concreteBalance struct {
acntID string
blnCfg *utils.Balance
fltrS *engine.FilterS
connMgr *engine.ConnManager

View File

@@ -31,7 +31,7 @@ import (
)
// newAccountBalances constructs accountBalances
func newBalanceOperators(blnCfgs []*utils.Balance,
func newBalanceOperators(acntID string, blnCfgs []*utils.Balance,
fltrS *engine.FilterS, connMgr *engine.ConnManager,
attrSConns, rateSConns []string) (blncOpers []balanceOperator, err error) {
@@ -41,7 +41,7 @@ func newBalanceOperators(blnCfgs []*utils.Balance,
if blnCfg.Type != utils.MetaConcrete {
continue
}
blncOpers[i] = newConcreteBalanceOperator(blnCfg,
blncOpers[i] = newConcreteBalanceOperator(acntID, blnCfg,
fltrS, connMgr, attrSConns, rateSConns)
cncrtBlncs = append(cncrtBlncs, blncOpers[i].(*concreteBalance))
}
@@ -50,8 +50,8 @@ func newBalanceOperators(blnCfgs []*utils.Balance,
if blnCfg.Type == utils.MetaConcrete {
continue
}
if blncOpers[i], err = newBalanceOperator(blnCfg, cncrtBlncs, fltrS, connMgr,
attrSConns, rateSConns); err != nil {
if blncOpers[i], err = newBalanceOperator(acntID, blnCfg, cncrtBlncs,
fltrS, connMgr, attrSConns, rateSConns); err != nil {
return
}
}
@@ -61,16 +61,16 @@ func newBalanceOperators(blnCfgs []*utils.Balance,
// newBalanceOperator instantiates balanceOperator interface
// cncrtBlncs are needed for abstract balance debits
func newBalanceOperator(blncCfg *utils.Balance, cncrtBlncs []*concreteBalance,
func newBalanceOperator(acntID string, blncCfg *utils.Balance, cncrtBlncs []*concreteBalance,
fltrS *engine.FilterS, connMgr *engine.ConnManager,
attrSConns, rateSConns []string) (bP balanceOperator, err error) {
switch blncCfg.Type {
default:
return nil, fmt.Errorf("unsupported balance type: <%s>", blncCfg.Type)
case utils.MetaConcrete:
return newConcreteBalanceOperator(blncCfg, fltrS, connMgr, attrSConns, rateSConns), nil
return newConcreteBalanceOperator(acntID, blncCfg, fltrS, connMgr, attrSConns, rateSConns), nil
case utils.MetaAbstract:
return newAbstractBalanceOperator(blncCfg, cncrtBlncs, fltrS, connMgr, attrSConns, rateSConns), nil
return newAbstractBalanceOperator(acntID, blncCfg, cncrtBlncs, fltrS, connMgr, attrSConns, rateSConns), nil
}
}

View File

@@ -56,7 +56,7 @@ func TestNewAccountBalanceOperators(t *testing.T) {
}
filters := engine.NewFilterS(config.NewDefaultCGRConfig(), nil, nil)
concrete, err := newBalanceOperator(acntPrf.Balances["BL1"], nil, filters, nil, nil, nil)
concrete, err := newBalanceOperator(acntPrf.ID, acntPrf.Balances["BL1"], nil, filters, nil, nil, nil)
if err != nil {
t.Error(err)
}
@@ -64,12 +64,13 @@ func TestNewAccountBalanceOperators(t *testing.T) {
cncrtBlncs = append(cncrtBlncs, concrete.(*concreteBalance))
expected := &abstractBalance{
acntID: acntPrf.ID,
blnCfg: acntPrf.Balances["BL0"],
fltrS: filters,
cncrtBlncs: cncrtBlncs,
}
blnCfgs := []*utils.Balance{acntPrf.Balances["BL0"], acntPrf.Balances["BL1"]}
if blcOp, err := newBalanceOperators(blnCfgs, filters, nil,
if blcOp, err := newBalanceOperators(acntPrf.ID, blnCfgs, filters, nil,
nil, nil); err != nil {
t.Error(err)
} else {
@@ -84,7 +85,7 @@ func TestNewAccountBalanceOperators(t *testing.T) {
acntPrf.Balances["BL1"].Type = "INVALID_TYPE"
expectedErr := "unsupported balance type: <INVALID_TYPE>"
if _, err := newBalanceOperators(blnCfgs, filters, nil,
if _, err := newBalanceOperators(acntPrf.ID, blnCfgs, filters, nil,
nil, nil); err == nil || err.Error() != expectedErr {
t.Errorf("Expected %+v, received %+v", expectedErr, err)
}
@@ -224,7 +225,10 @@ func TestDebitUsageFromConcretes(t *testing.T) {
fltrS: filterS,
}
expectedEvCh := &utils.EventCharges{
Concretes: utils.NewDecimal(710, 0),
Concretes: utils.NewDecimal(710, 0),
Accounting: make(map[string]*utils.AccountCharge),
UnitFactors: make(map[string]*utils.UnitFactor),
Rating: make(map[string]*utils.RateSInterval),
}
if evCh, err := debitAbstractsFromConcretes([]*concreteBalance{cb1, cb2}, decimal.New(700, 0), &utils.CostIncrement{
@@ -292,7 +296,10 @@ func TestDebitUsageFromConcretesFromRateS(t *testing.T) {
}
expectedEvCh := &utils.EventCharges{
Concretes: utils.NewDecimal(100, 0),
Concretes: utils.NewDecimal(100, 0),
Accounting: make(map[string]*utils.AccountCharge),
UnitFactors: make(map[string]*utils.UnitFactor),
Rating: make(map[string]*utils.RateSInterval),
}
if evCh, err := debitAbstractsFromConcretes([]*concreteBalance{cb1, cb2}, decimal.New(700, 0), &utils.CostIncrement{

View File

@@ -58,9 +58,6 @@ func (ap *AccountProfile) RestoreFromBackup(abb AccountBalancesBackup) {
}
}
// AccountBalanceBackups is used to create balance snapshots as backups
type AccountBalancesBackup map[string]*decimal.Big
// AccountBalancesBackup returns a backup of all balance values
func (ap *AccountProfile) AccountBalancesBackup() (abb AccountBalancesBackup) {
if ap.Balances != nil {
@@ -72,6 +69,9 @@ func (ap *AccountProfile) AccountBalancesBackup() (abb AccountBalancesBackup) {
return
}
// AccountBalanceBackups is used to create balance snapshots as backups
type AccountBalancesBackup map[string]*decimal.Big
// NewDefaultBalance returns a balance with default costIncrements
func NewDefaultBalance(id string) *Balance {
const torFltr = "*string:~*req.ToR:"

View File

@@ -967,6 +967,7 @@ const (
RecurrentFee = "RecurrentFee"
Diktats = "Diktats"
BalanceIDs = "BalanceIDs"
MetaCostIncrement = "*costIncrement"
)
// Migrator Action

View File

@@ -24,7 +24,11 @@ import (
// NewEventChargers instantiates the EventChargers in a central place
func NewEventCharges() (ec *EventCharges) {
ec = new(EventCharges)
ec = &EventCharges{
Accounting: make(map[string]*AccountCharge),
UnitFactors: make(map[string]*UnitFactor),
Rating: make(map[string]*RateSInterval),
}
return
}
@@ -36,8 +40,9 @@ type EventCharges struct {
ChargingIntervals []*ChargingInterval
Accounts []*AccountProfile
Accounting *ChargingAccountS
Rating *ChargingRateS
Accounting map[string]*AccountCharge
UnitFactors map[string]*UnitFactor
Rating map[string]*RateSInterval
}
// Merge will merge the event charges into existing
@@ -83,3 +88,27 @@ type ExtEventCharges struct {
Abstracts *float64
Concretes *float64
}
type ChargingInterval struct {
Increments []*ChargingIncrement // specific increments applied to this interval
CompressFactor int
}
// ChargingIncrement represents one unit charged inside an interval
type ChargingIncrement struct {
Units *Decimal
AccountChargeID string // Account charging information
CompressFactor int
}
// AccountCharge represents one Account charge
type AccountCharge struct {
AccountID string
BalanceID string
Units *Decimal
BalanceLimit *Decimal // the minimum balance value accepted
UnitFactorID string // identificator in ChargingUnitFactors
AttributeIDs []string // list of attribute profiles matched
RatingID string // identificator in cost increments
JoinedChargeIDs []string // identificator of extra account charges
}

View File

@@ -26,7 +26,11 @@ import (
)
func TestECNewEventCharges(t *testing.T) {
expected := &EventCharges{}
expected := &EventCharges{
Accounting: make(map[string]*AccountCharge),
UnitFactors: make(map[string]*UnitFactor),
Rating: make(map[string]*RateSInterval),
}
received := NewEventCharges()
if !reflect.DeepEqual(expected, received) {

View File

@@ -17,29 +17,3 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package utils
type ChargingRateS map[string]*RateSInterval
type ChargingInterval struct {
Increments []*ChargingIncrement // specific increments applied to this interval
CompressFactor int
}
// ChargingIncrement represents one unit charged inside an interval
type ChargingIncrement struct {
Units *Decimal
AccountChargeID string // Account charged information
CompressFactor int
}
type ChargingAccountS map[string]*AccountCharge
// AccountCharge represents one Account charge
type AccountCharge struct {
BalanceID string
Units *Decimal
UnitFactorID string // identificator in unit factors
AttributeIDs []string // list of attribute profiles matched
CostID string // identificator in cost increments
JoinedChargeIDs []string // identificator of extra account charges
}