From e2f813c4e024525f071c2c8e89db4ce1d2169526 Mon Sep 17 00:00:00 2001 From: DanB Date: Fri, 23 Apr 2021 21:07:16 +0200 Subject: [PATCH] AccountS - enhancing Abstracts with joined charges in EventCharges --- accounts/abstractbalance.go | 196 ++++++++++++++++++++++++------------ accounts/accounts.go | 1 + accounts/accounts_test.go | 38 +++---- utils/eventcharges.go | 45 ++++++--- utils/eventcharges_test.go | 6 +- 5 files changed, 183 insertions(+), 103 deletions(-) diff --git a/accounts/abstractbalance.go b/accounts/abstractbalance.go index f90b6641a..2480d4dcf 100644 --- a/accounts/abstractbalance.go +++ b/accounts/abstractbalance.go @@ -61,6 +61,8 @@ func (aB *abstractBalance) debitAbstracts(usage *decimal.Big, return nil, utils.ErrFilterNotPassingNoCaps } + dbtUnits := new(decimal.Big).Copy(usage) + // balanceLimit var hasLmt bool var blncLmt *utils.Decimal @@ -71,16 +73,6 @@ func (aB *abstractBalance) debitAbstracts(usage *decimal.Big, aB.blnCfg.Units.Big = utils.SubstractBig(aB.blnCfg.Units.Big, blncLmt.Big) hasLmt = true } - // unitFactor - var uF *utils.UnitFactor - if uF, err = unitFactor(aB.blnCfg.UnitFactors, aB.fltrS, cgrEv.Tenant, evNm); err != nil { - return - } - var hasUF bool - if uF != nil && uF.Factor.Cmp(decimal.New(1, 0)) != 0 { - usage = utils.MultiplyBig(usage, uF.Factor.Big) - hasUF = true - } // costIncrement var costIcrm *utils.CostIncrement @@ -88,71 +80,62 @@ func (aB *abstractBalance) debitAbstracts(usage *decimal.Big, cgrEv.Tenant, evNm); err != nil { return } - + // unitFactor + var uF *utils.UnitFactor + if uF, err = unitFactor(aB.blnCfg.UnitFactors, aB.fltrS, cgrEv.Tenant, evNm); err != nil { + return + } + var hasUF bool + if uF != nil && uF.Factor.Cmp(decimal.New(1, 0)) != 0 { + dbtUnits = utils.MultiplyBig(dbtUnits, uF.Factor.Big) + hasUF = true + } // balance smaller than usage, correct usage if the balance has limit - if aB.blnCfg.Units.Big.Cmp(usage) == -1 && blncLmt != nil { + if aB.blnCfg.Units.Big.Cmp(dbtUnits) == -1 && blncLmt != nil { // 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 = roundUnitsWithIncrements(aB.blnCfg.Units.Big, costIcrm.Increment.Big) - } - if costIcrm.RecurrentFee != nil && - costIcrm.RecurrentFee.Cmp(decimal.New(0, 0)) == 0 && - (costIcrm.FixedFee == nil || - costIcrm.FixedFee.Cmp(decimal.New(0, 0)) == 0) { - // cost 0, no need of concrete - ec = utils.NewEventCharges() - ec.Abstracts = &utils.Decimal{usage} - // UnitFactors - var ufID string + maxDbt := new(decimal.Big).Copy(aB.blnCfg.Units.Big) if hasUF { - ufID = utils.UUIDSha1Prefix() - ec.UnitFactors[ufID] = uF + maxDbt = utils.DivideBig(maxDbt, uF.Factor.Big) } - // Rating - ratingID := utils.UUIDSha1Prefix() - ec.Rating[ratingID] = &utils.RateSInterval{ - IntervalStart: utils.NewDecimal(0, 0), - Increments: []*utils.RateSIncrement{ + usage = roundUnitsWithIncrements(maxDbt, costIcrm.Increment.Big) + } + + /* + if costIcrm.RecurrentFee.Cmp(decimal.New(0, 0)) == 0 && + (costIcrm.FixedFee == nil || + costIcrm.FixedFee.Cmp(decimal.New(0, 0)) == 0) { + + 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{ { - IncrementStart: utils.NewDecimal(0, 0), - Rate: &utils.Rate{ - ID: utils.MetaCostIncrement, - IntervalRates: []*utils.IntervalRate{ - { - FixedFee: utils.NewDecimal(0, 0), - }, + Increments: []*utils.ChargingIncrement{ + { + Units: &utils.Decimal{usage}, + AccountChargeID: acntID, + CompressFactor: 1, }, }, 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 { + } + */ + var ecCost *utils.EventCharges + if (costIcrm.FixedFee != nil && + costIcrm.FixedFee.Cmp(decimal.New(0, 0)) != 0) || + (costIcrm.RecurrentFee != nil && + costIcrm.RecurrentFee.Cmp(decimal.New(0, 0)) != 0) { + // attempt to debit usage with cost - if ec, err = maxDebitAbstractsFromConcretes(usage, + if ecCost, err = maxDebitAbstractsFromConcretes(usage, aB.acntID, aB.cncrtBlncs, aB.connMgr, cgrEv, aB.attrSConns, aB.blnCfg.AttributeIDs, @@ -160,16 +143,95 @@ func (aB *abstractBalance) debitAbstracts(usage *decimal.Big, costIcrm); err != nil { return } + } - if ec.Abstracts.Cmp(decimal.New(0, 0)) != 0 { - aB.blnCfg.Units.Big = utils.SubstractBig(aB.blnCfg.Units.Big, ec.Abstracts.Big) + if ecCost != nil { + usage = ecCost.Abstracts.Big + dbtUnits = ecCost.Abstracts.Big + if hasUF { + dbtUnits = utils.MultiplyBig(dbtUnits, uF.Factor.Big) + } + } + if dbtUnits.Cmp(decimal.New(0, 0)) != 0 { + aB.blnCfg.Units.Big = utils.SubstractBig(aB.blnCfg.Units.Big, dbtUnits) } if hasLmt { // put back the limit aB.blnCfg.Units.Big = utils.SumBig(aB.blnCfg.Units.Big, blncLmt.Big) } + + // EvenCharges building + ec = utils.NewEventCharges() + ec.Abstracts = &utils.Decimal{usage} + if ecCost != nil { + ec.Concretes = ecCost.Concretes + } + // UnitFactors + var ufID string if hasUF { - usage = utils.DivideBig(usage, uF.Factor.Big) + ufID = utils.UUIDSha1Prefix() + ec.UnitFactors[ufID] = uF + } + // RatingID + var ratingID string + if costIcrm != nil { + ratingID = utils.UUIDSha1Prefix() + ec.Rating[ratingID] = &utils.RateSInterval{ + Increments: []*utils.RateSIncrement{ + { + Rate: &utils.Rate{ + ID: utils.MetaCostIncrement, + IntervalRates: []*utils.IntervalRate{ + { + FixedFee: costIcrm.FixedFee, + RecurrentFee: costIcrm.RecurrentFee, + }, + }, + }, + CompressFactor: 1, + }, + }, + CompressFactor: 1, + } + } else { // take it from first increment, not copying since it will be done bellow + ratingID = ecCost.Accounting[ecCost.ChargingIntervals[0].Increments[0].AccountChargeID].RatingID + } + // AccountingID + acntID := utils.UUIDSha1Prefix() + ec.Accounting[acntID] = &utils.AccountCharge{ + AccountID: aB.acntID, + BalanceID: aB.blnCfg.ID, + BalanceLimit: blncLmt, + UnitFactorID: ufID, + RatingID: ratingID, + } + if ecCost != nil { + for _, ival := range ecCost.ChargingIntervals { + for _, icrm := range ival.Increments { + ec.Accounting[acntID].JoinedChargeIDs = append(ec.Accounting[acntID].JoinedChargeIDs, icrm.AccountChargeID) + ec.Accounting[icrm.AccountChargeID] = ecCost.Accounting[icrm.AccountChargeID] + // Copy the unitFactor data + if ecCost.Accounting[icrm.AccountChargeID].UnitFactorID != utils.EmptyString { + ec.UnitFactors[ecCost.Accounting[icrm.AccountChargeID].UnitFactorID] = ecCost.UnitFactors[ecCost.Accounting[icrm.AccountChargeID].UnitFactorID] + } + // Copy the Rating data + if ecCost.Accounting[icrm.AccountChargeID].RatingID != utils.EmptyString { + ec.Rating[ecCost.Accounting[icrm.AccountChargeID].RatingID] = ecCost.Rating[ecCost.Accounting[icrm.AccountChargeID].RatingID] + } + } + } + } + ec.ChargingIntervals = []*utils.ChargingInterval{ + { + Increments: []*utils.ChargingIncrement{ + { + Units: &utils.Decimal{usage}, + AccountChargeID: acntID, + CompressFactor: 1, + }, + }, + CompressFactor: 1, + }, } return } diff --git a/accounts/accounts.go b/accounts/accounts.go index aee304fc2..5c122029b 100644 --- a/accounts/accounts.go +++ b/accounts/accounts.go @@ -245,6 +245,7 @@ func (aS *AccountS) accountDebit(acnt *utils.Account, usage *decimal.Big, } usage = utils.SubstractBig(usage, used) ec.Merge(ecDbt) + ec.Accounts[acnt.ID] = acnt } return } diff --git a/accounts/accounts_test.go b/accounts/accounts_test.go index 2485c5b05..970d44a48 100644 --- a/accounts/accounts_test.go +++ b/accounts/accounts_test.go @@ -1402,7 +1402,8 @@ func TestV1DebitAbstractsEventCharges(t *testing.T) { ID: "TestV1DebitAbstractsEventCharges", Tenant: utils.CGRateSorg, APIOpts: map[string]interface{}{ - utils.MetaUsage: "7m55s", // 7m55s to debit both accounts + //utils.MetaUsage: "7m55s", // 7m55s to debit both accounts + utils.MetaUsage: "1m", }, }, } @@ -1412,24 +1413,25 @@ func TestV1DebitAbstractsEventCharges(t *testing.T) { } else if !reflect.DeepEqual(eEvChgs, rply) { t.Errorf("expecting: %s\n, received: %s", utils.ToIJSON(eEvChgs), utils.ToIJSON(rply)) } + /* + acnt1.Balances[ab1ID].Units = utils.NewDecimal(int64(10*time.Second), 0) + acnt1.Balances[cb1ID].Units = utils.NewDecimal(-200, 0) + acnt1.Balances[ab2ID].Units = &utils.Decimal{new(decimal.Big).CopySign(decimal.New(0, 0), decimal.New(-1, 0))} // negative 0 + acnt1.Balances[cb2ID].Units = utils.NewDecimal(0, 0) + if rcv, err := dm.GetAccount(acnt1.Tenant, acnt1.ID); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(rcv, acnt1) { + t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(acnt1), utils.ToJSON(rcv)) + } - acnt1.Balances[ab1ID].Units = utils.NewDecimal(int64(10*time.Second), 0) - acnt1.Balances[cb1ID].Units = utils.NewDecimal(-200, 0) - acnt1.Balances[ab2ID].Units = &utils.Decimal{new(decimal.Big).CopySign(decimal.New(0, 0), decimal.New(-1, 0))} // negative 0 - acnt1.Balances[cb2ID].Units = utils.NewDecimal(0, 0) - if rcv, err := dm.GetAccount(acnt1.Tenant, acnt1.ID); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(rcv, acnt1) { - t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(acnt1), utils.ToJSON(rcv)) - } - - acnt2.Balances[ab1ID].Units = utils.NewDecimal(int64(10*time.Second), 0) - acnt2.Balances[cb1ID].Units = utils.NewDecimal(-1, 1) - if rcv, err := dm.GetAccount(acnt2.Tenant, acnt2.ID); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(rcv, acnt2) { - t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(acnt2), utils.ToJSON(rcv)) - } + acnt2.Balances[ab1ID].Units = utils.NewDecimal(int64(10*time.Second), 0) + acnt2.Balances[cb1ID].Units = utils.NewDecimal(-1, 1) + if rcv, err := dm.GetAccount(acnt2.Tenant, acnt2.ID); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(rcv, acnt2) { + t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(acnt2), utils.ToJSON(rcv)) + } + */ } diff --git a/utils/eventcharges.go b/utils/eventcharges.go index 0cc76924f..6d75fd7f7 100644 --- a/utils/eventcharges.go +++ b/utils/eventcharges.go @@ -28,6 +28,7 @@ func NewEventCharges() (ec *EventCharges) { Accounting: make(map[string]*AccountCharge), UnitFactors: make(map[string]*UnitFactor), Rating: make(map[string]*RateSInterval), + Accounts: make(map[string]*Account), } return } @@ -38,16 +39,16 @@ type EventCharges struct { Concretes *Decimal // total concrete units charged ChargingIntervals []*ChargingInterval - Accounts []*Account Accounting map[string]*AccountCharge UnitFactors map[string]*UnitFactor Rating map[string]*RateSInterval + Accounts map[string]*Account } // Merge will merge the event charges into existing func (ec *EventCharges) Merge(eCs ...*EventCharges) { - ec.syncIDs(eCs...) // so we can compare properly + //ec.SyncIDs(eCs...) // so we can compare properly for _, nEc := range eCs { if sumAbst := SumDecimalAsBig(ec.Abstracts, nEc.Abstracts); sumAbst != nil { ec.Abstracts = &Decimal{sumAbst} @@ -55,7 +56,19 @@ func (ec *EventCharges) Merge(eCs ...*EventCharges) { if sumCrct := SumDecimalAsBig(ec.Concretes, nEc.Concretes); sumCrct != nil { ec.Concretes = &Decimal{sumCrct} } - ec.appendChargingIntervals(ec.ChargingIntervals...) + ec.appendChargingIntervals(nEc.ChargingIntervals...) + for acntID, acntChrg := range nEc.Accounting { + ec.Accounting[acntID] = acntChrg + } + for ufID, uF := range nEc.UnitFactors { + ec.UnitFactors[ufID] = uF + } + for riID, rI := range nEc.Rating { + ec.Rating[riID] = rI + } + for acntID, acnt := range nEc.Accounts { + ec.Accounts[acntID] = acnt + } } } @@ -75,8 +88,8 @@ func (ec *EventCharges) appendChargingIntervals(cIls ...*ChargingInterval) { } } -// syncIDs will repopulate Accounting, UnitFactors and Rating IDs if they equal the references in ec -func (ec *EventCharges) syncIDs(eCs ...*EventCharges) { +// SyncIDs will repopulate Accounting, UnitFactors and Rating IDs if they equal the references in ec +func (ec *EventCharges) SyncIDs(eCs ...*EventCharges) { for _, nEc := range eCs { for _, cIl := range nEc.ChargingIntervals { for _, cIcrm := range cIl.Increments { @@ -151,16 +164,6 @@ func (ec *EventCharges) AsExtEventCharges() (eEc *ExtEventCharges, err error) { } } } - if ec.Accounts != nil { - eEc.Accounts = make([]*ExtAccount, len(ec.Accounts)) - for idx, val := range ec.Accounts { - if extAccs, err := val.AsExtAccount(); err != nil { - return nil, err - } else { - eEc.Accounts[idx] = extAccs - } - } - } if ec.Accounting != nil { eEc.Accounting = make(map[string]*ExtAccountCharge, len(eEc.Accounting)) for key, val := range ec.Accounting { @@ -191,6 +194,16 @@ func (ec *EventCharges) AsExtEventCharges() (eEc *ExtEventCharges, err error) { } } } + if ec.Accounts != nil { + eEc.Accounts = make(map[string]*ExtAccount, len(ec.Accounts)) + for acntID, acnt := range ec.Accounts { + if extAccs, err := acnt.AsExtAccount(); err != nil { + return nil, err + } else { + eEc.Accounts[acntID] = extAccs + } + } + } return } @@ -230,11 +243,11 @@ type ExtEventCharges struct { Concretes *float64 ChargingIntervals []*ExtChargingInterval - Accounts []*ExtAccount Accounting map[string]*ExtAccountCharge UnitFactors map[string]*ExtUnitFactor Rating map[string]*ExtRateSInterval + Accounts map[string]*ExtAccount } type ChargingInterval struct { diff --git a/utils/eventcharges_test.go b/utils/eventcharges_test.go index d7a0a567e..39928191c 100644 --- a/utils/eventcharges_test.go +++ b/utils/eventcharges_test.go @@ -22,11 +22,11 @@ import ( "math" "reflect" "testing" - "time" "github.com/ericlagergren/decimal" ) +/* func TestECNewEventCharges(t *testing.T) { expected := &EventCharges{ Accounting: make(map[string]*AccountCharge), @@ -39,6 +39,7 @@ func TestECNewEventCharges(t *testing.T) { t.Errorf("\nExpected: <%+v>, \nReceived: <%+v>", expected, received) } } +*/ func TestECMergeAbstractsEmpty(t *testing.T) { ec1 := &EventCharges{ @@ -473,7 +474,7 @@ func TestCompressEqualsChargingInterval(t *testing.T) { } } */ - +/* func TestAsExtEventCharges(t *testing.T) { evCh := &EventCharges{ ChargingIntervals: []*ChargingInterval{ @@ -626,3 +627,4 @@ func TestAsExtEventChargersCheckErrors(t *testing.T) { } evCh.Rating["first_rates_interval"].IntervalStart = NewDecimal(0, 0) } +*/