diff --git a/accounts/abstractbalance.go b/accounts/abstractbalance.go index f12956d51..db3a8e204 100644 --- a/accounts/abstractbalance.go +++ b/accounts/abstractbalance.go @@ -43,7 +43,7 @@ type abstractBalance struct { // debitUsage implements the balanceOperator interface func (aB *abstractBalance) debitUsage(usage *utils.Decimal, - cgrEv *utils.CGREvent) (dbted *utils.Decimal, ec *utils.EventCharges, err error) { + cgrEv *utils.CGREvent) (ec *utils.EventCharges, err error) { evNm := utils.MapStorage{ utils.MetaOpts: cgrEv.Opts, @@ -55,7 +55,7 @@ func (aB *abstractBalance) debitUsage(usage *utils.Decimal, if pass, err = aB.fltrS.Pass(cgrEv.Tenant, aB.blnCfg.FilterIDs, evNm); err != nil { return } else if !pass { - return nil, nil, utils.ErrFilterNotPassingNoCaps + return nil, utils.ErrFilterNotPassingNoCaps } // balanceLimit @@ -95,7 +95,7 @@ func (aB *abstractBalance) debitUsage(usage *utils.Decimal, } // attempt to debit usage with cost - if dbted, ec, err = maxDebitUsageFromConcretes(aB.cncrtBlncs, usage, + if ec, err = maxDebitUsageFromConcretes(aB.cncrtBlncs, usage, aB.connMgr, cgrEv, aB.attrSConns, aB.blnCfg.AttributeIDs, aB.rateSConns, aB.blnCfg.RateProfileIDs, @@ -103,8 +103,8 @@ func (aB *abstractBalance) debitUsage(usage *utils.Decimal, return } - if dbted.Cmp(decimal.New(0, 0)) != 0 { - aB.blnCfg.Units.Big = utils.SubstractBig(aB.blnCfg.Units.Big, dbted.Big) + if ec.Usage.Cmp(decimal.New(0, 0)) != 0 { + aB.blnCfg.Units.Big = utils.SubstractBig(aB.blnCfg.Units.Big, ec.Usage) } if hasLmt { // put back the limit aB.blnCfg.Units.Big = utils.SumBig(aB.blnCfg.Units.Big, blncLmt.Big) diff --git a/accounts/abstractbalance_test.go b/accounts/abstractbalance_test.go index acaabd5b6..ae6bd2aa8 100644 --- a/accounts/abstractbalance_test.go +++ b/accounts/abstractbalance_test.go @@ -24,6 +24,7 @@ import ( "github.com/cgrates/cgrates/engine" "github.com/cgrates/cgrates/utils" + "github.com/ericlagergren/decimal" ) func TestdebitUsageFromConcretes(t *testing.T) { @@ -133,11 +134,11 @@ func TestABDebitUsage(t *testing.T) { fltrS: new(engine.FilterS), } - if dbted, _, err := aB.debitUsage(utils.NewDecimal(int64(30*time.Second), 0), + if ec, err := aB.debitUsage(utils.NewDecimal(int64(30*time.Second), 0), new(utils.CGREvent)); err != nil { t.Error(err) - } else if dbted.Compare(utils.NewDecimal(int64(30*time.Second), 0)) != 0 { - t.Errorf("Unexpected debited units: %s", dbted) + } else if ec.Usage.Cmp(decimal.New(int64(30*time.Second), 0)) != 0 { + t.Errorf("Unexpected debited units: %s", ec.Usage) } else if aB.blnCfg.Units.Compare(utils.NewDecimal(int64(30*time.Second), 0)) != 0 { t.Errorf("Unexpected units in abstract balance: %s", aB.blnCfg.Units) } else if aB.cncrtBlncs[0].blnCfg.Units.Compare(utils.NewDecimal(20, 0)) != 0 { @@ -148,11 +149,11 @@ func TestABDebitUsage(t *testing.T) { aB.blnCfg.Units = utils.NewDecimal(int64(time.Duration(60*time.Second)), 0) aB.cncrtBlncs[0].blnCfg.Units = utils.NewDecimal(29, 0) // not enough concrete - if dbted, _, err := aB.debitUsage(utils.NewDecimal(int64(30*time.Second), 0), + if ec, err := aB.debitUsage(utils.NewDecimal(int64(30*time.Second), 0), new(utils.CGREvent)); err != nil { t.Error(err) - } else if dbted.Compare(utils.NewDecimal(int64(29*time.Second), 0)) != 0 { - t.Errorf("Unexpected debited units: %s", dbted) + } else if ec.Usage.Cmp(decimal.New(int64(29*time.Second), 0)) != 0 { + t.Errorf("Unexpected debited units: %s", ec.Usage) } else if aB.blnCfg.Units.Compare(utils.NewDecimal(int64(time.Duration(31*time.Second)), 0)) != 0 { // used 29 units t.Errorf("Unexpected units in abstract balance: %s", aB.blnCfg.Units) } else if aB.cncrtBlncs[0].blnCfg.Units.Compare(utils.NewDecimal(0, 0)) != 0 { @@ -162,11 +163,11 @@ func TestABDebitUsage(t *testing.T) { // limited by concrete aB.cncrtBlncs[0].blnCfg.Units = utils.NewDecimal(0, 0) // not enough concrete - if dbted, _, err := aB.debitUsage(utils.NewDecimal(int64(30*time.Second), 0), + if ec, err := aB.debitUsage(utils.NewDecimal(int64(30*time.Second), 0), new(utils.CGREvent)); err != nil { t.Error(err) - } else if dbted.Compare(utils.NewDecimal(0, 0)) != 0 { - t.Errorf("Unexpected debited units: %s", dbted) + } else if ec.Usage.Cmp(decimal.New(0, 0)) != 0 { + t.Errorf("Unexpected debited units: %s", ec.Usage) } else if aB.blnCfg.Units.Compare(utils.NewDecimal(int64(31*time.Second), 0)) != 0 { // same as above t.Errorf("Unexpected units in abstract balance: %s", aB.blnCfg.Units) } else if aB.cncrtBlncs[0].blnCfg.Units.Compare(utils.NewDecimal(0, 0)) != 0 { // same as above @@ -177,11 +178,11 @@ func TestABDebitUsage(t *testing.T) { aB.blnCfg.Units = utils.NewDecimal(int64(time.Duration(29*time.Second)), 0) // not enough abstract aB.cncrtBlncs[0].blnCfg.Units = utils.NewDecimal(60, 0) - if dbted, _, err := aB.debitUsage(utils.NewDecimal(int64(30*time.Second), 0), + if ec, err := aB.debitUsage(utils.NewDecimal(int64(30*time.Second), 0), new(utils.CGREvent)); err != nil { t.Error(err) - } else if dbted.Compare(utils.NewDecimal(int64(29*time.Second), 0)) != 0 { - t.Errorf("Unexpected debited units: %s", dbted) + } else if ec.Usage.Cmp(decimal.New(int64(29*time.Second), 0)) != 0 { + t.Errorf("Unexpected debited units: %s", ec.Usage) } else if aB.blnCfg.Units.Compare(utils.NewDecimal(0, 0)) != 0 { // should be all used t.Errorf("Unexpected units in abstract balance: %s", aB.blnCfg.Units) } else if aB.cncrtBlncs[0].blnCfg.Units.Compare(utils.NewDecimal(31, 0)) != 0 { diff --git a/accounts/accounts.go b/accounts/accounts.go index 01d86eb7d..6af34aa9f 100644 --- a/accounts/accounts.go +++ b/accounts/accounts.go @@ -151,14 +151,15 @@ func (aS *AccountS) accountProcessEvent(acnt *utils.AccountProfile, if usage.Big.Cmp(decimal.New(0, 0)) == 0 { return // no more debit } - var dbted *utils.Decimal - if dbted, _, err = blncOper.debitUsage(usage, cgrEv); err != nil { + var ecDbt *utils.EventCharges + if ec, err = blncOper.debitUsage(usage, cgrEv); err != nil { if err == utils.ErrFilterNotPassingNoCaps { err = nil continue } } - usage.Big = utils.SubstractBig(usage.Big, dbted.Big) + usage.Big = utils.SubstractBig(usage.Big, ecDbt.Usage) + ec.Merge(ecDbt) } return } diff --git a/accounts/concretebalance.go b/accounts/concretebalance.go index fdc62887f..d35242feb 100644 --- a/accounts/concretebalance.go +++ b/accounts/concretebalance.go @@ -111,7 +111,7 @@ func (cB *concreteBalance) debitUnits(dUnts *utils.Decimal, tnt string, // debit implements the balanceOperator interface func (cB *concreteBalance) debitUsage(usage *utils.Decimal, - cgrEv *utils.CGREvent) (dbted *utils.Decimal, ec *utils.EventCharges, err error) { + cgrEv *utils.CGREvent) (ec *utils.EventCharges, err error) { evNm := utils.MapStorage{ utils.MetaOpts: cgrEv.Opts, @@ -123,7 +123,7 @@ func (cB *concreteBalance) debitUsage(usage *utils.Decimal, if pass, err = cB.fltrS.Pass(cgrEv.Tenant, cB.blnCfg.FilterIDs, evNm); err != nil { return } else if !pass { - return nil, nil, utils.ErrFilterNotPassingNoCaps + return nil, utils.ErrFilterNotPassingNoCaps } // costIncrement @@ -145,7 +145,8 @@ func (cB *concreteBalance) debitUsage(usage *utils.Decimal, } } - return maxDebitUsageFromConcretes([]*concreteBalance{cB}, usage, + return maxDebitUsageFromConcretes( + []*concreteBalance{cB}, usage, cB.connMgr, cgrEv, cB.attrSConns, cB.blnCfg.AttributeIDs, cB.rateSConns, cB.blnCfg.RateProfileIDs, diff --git a/accounts/libaccounts.go b/accounts/libaccounts.go index 14a695ad3..86332a461 100644 --- a/accounts/libaccounts.go +++ b/accounts/libaccounts.go @@ -75,7 +75,7 @@ func newBalanceOperator(blncCfg *utils.Balance, cncrtBlncs []*concreteBalance, // balanceOperator is the implementation of a balance type type balanceOperator interface { - debitUsage(usage *utils.Decimal, cgrEv *utils.CGREvent) (dbted *utils.Decimal, ec *utils.EventCharges, err error) + debitUsage(usage *utils.Decimal, cgrEv *utils.CGREvent) (ec *utils.EventCharges, err error) } // roundUsageWithIncrements rounds the usage based on increments @@ -234,7 +234,7 @@ func debitUsageFromConcretes(cncrtBlncs []*concreteBalance, usage *utils.Decimal func maxDebitUsageFromConcretes(cncrtBlncs []*concreteBalance, usage *utils.Decimal, connMgr *engine.ConnManager, cgrEv *utils.CGREvent, attrSConns, attributeIDs, rateSConns, rpIDs []string, - costIcrm *utils.CostIncrement) (dbtedUsage *utils.Decimal, ec *utils.EventCharges, err error) { + costIcrm *utils.CostIncrement) (ec *utils.EventCharges, err error) { // process AttributeS if needed if costIcrm.RecurrentFee.Cmp(decimal.New(-1, 0)) == 0 && @@ -260,7 +260,7 @@ func maxDebitUsageFromConcretes(cncrtBlncs []*concreteBalance, usage *utils.Deci restoreUnitsFromClones(cncrtBlncs, origConcrtUnts) } if i == maxItr { - return nil, nil, utils.ErrMaxIncrementsExceeded + return nil, utils.ErrMaxIncrementsExceeded } qriedUsage := usage.Big // so we can detect loops if err = debitUsageFromConcretes(cncrtBlncs, usage, costIcrm, cgrEv, @@ -308,5 +308,5 @@ func maxDebitUsageFromConcretes(cncrtBlncs []*concreteBalance, usage *utils.Deci usagePaid = decimal.New(0, 0) } restoreUnitsFromClones(cncrtBlncs, paidConcrtUnts) - return &utils.Decimal{usagePaid}, nil, nil + return &utils.EventCharges{Usage: usagePaid}, nil } diff --git a/utils/eventcharges.go b/utils/eventcharges.go index 06cd18742..cc771bcdf 100644 --- a/utils/eventcharges.go +++ b/utils/eventcharges.go @@ -19,14 +19,13 @@ along with this program. If not, see package utils import ( - "time" + "errors" "github.com/ericlagergren/decimal" ) // EventCharges records the charges applied to an Event type EventCharges struct { - StartTime *time.Time Usage *decimal.Big Cost *decimal.Big Charges []*ChargedInterval @@ -34,3 +33,41 @@ type EventCharges struct { Accounting *ChargedAccounting Rating *ChargedRating } + +// Merge will merge the event charges into existing +func (ec *EventCharges) Merge(eCs ...*EventCharges) { + for _, nEc := range eCs { + if ec.Usage == nil { + ec.Usage = nEc.Usage + continue + } + ec.Usage = SumBig(ec.Usage, nEc.Usage) + } +} + +// AsExtEventCharges converts EventCharges to ExtEventCharges +func (ec *EventCharges) AsExtEventCharges() (eEc *ExtEventCharges, err error) { + eEc = new(ExtEventCharges) + if ec.Usage != nil { + if flt, ok := ec.Usage.Float64(); !ok { + return nil, errors.New("cannot convert decimal Usage to float64") + } else { + eEc.Usage = &flt + } + } + if ec.Cost != nil { + if flt, ok := ec.Cost.Float64(); !ok { + return nil, errors.New("cannot convert decimal Cost to float64") + } else { + eEc.Cost = &flt + } + } + // add here code for the rest of the fields + return +} + +// ExtEventCharges is a generic EventCharges used in APIs +type ExtEventCharges struct { + Usage *float64 + Cost *float64 +}