diff --git a/accounts/abstractbalance.go b/accounts/abstractbalance.go index 3b659e8f2..14076d72f 100644 --- a/accounts/abstractbalance.go +++ b/accounts/abstractbalance.go @@ -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, diff --git a/accounts/abstractbalance_test.go b/accounts/abstractbalance_test.go index 738a20d17..d0c837811 100644 --- a/accounts/abstractbalance_test.go +++ b/accounts/abstractbalance_test.go @@ -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, diff --git a/accounts/accounts.go b/accounts/accounts.go index 6b4bf479c..7720746f2 100644 --- a/accounts/accounts.go +++ b/accounts/accounts.go @@ -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 diff --git a/accounts/concretebalance.go b/accounts/concretebalance.go index e81fc7541..ee2e2d482 100644 --- a/accounts/concretebalance.go +++ b/accounts/concretebalance.go @@ -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 diff --git a/accounts/libaccounts.go b/accounts/libaccounts.go index 4750803af..976dfb3c1 100644 --- a/accounts/libaccounts.go +++ b/accounts/libaccounts.go @@ -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 } } diff --git a/accounts/libaccounts_test.go b/accounts/libaccounts_test.go index 8917c3465..7cd6c0cf8 100644 --- a/accounts/libaccounts_test.go +++ b/accounts/libaccounts_test.go @@ -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: " - 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{ diff --git a/utils/accountprofile.go b/utils/accountprofile.go index 65a972125..68b14acda 100644 --- a/utils/accountprofile.go +++ b/utils/accountprofile.go @@ -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:" diff --git a/utils/consts.go b/utils/consts.go index 41b049ab9..450029b66 100755 --- a/utils/consts.go +++ b/utils/consts.go @@ -967,6 +967,7 @@ const ( RecurrentFee = "RecurrentFee" Diktats = "Diktats" BalanceIDs = "BalanceIDs" + MetaCostIncrement = "*costIncrement" ) // Migrator Action diff --git a/utils/eventcharges.go b/utils/eventcharges.go index 0d5353a32..7be63882b 100644 --- a/utils/eventcharges.go +++ b/utils/eventcharges.go @@ -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 +} diff --git a/utils/eventcharges_test.go b/utils/eventcharges_test.go index 8f3f926ed..1ace428e2 100644 --- a/utils/eventcharges_test.go +++ b/utils/eventcharges_test.go @@ -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) { diff --git a/utils/libeventcharges.go b/utils/libeventcharges.go index f1fc2e6c5..be2b7f8c9 100644 --- a/utils/libeventcharges.go +++ b/utils/libeventcharges.go @@ -17,29 +17,3 @@ along with this program. If not, see */ 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 -}