diff --git a/accounts/absolutebalance.go b/accounts/absolutebalance.go
deleted file mode 100644
index 963b9fd56..000000000
--- a/accounts/absolutebalance.go
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
-Real-time Online/Offline Charging System (OerS) for Telecom & ISP environments
-Copyright (C) ITsysCOM GmbH
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see
-*/
-
-package accounts
-
-import (
- "time"
-
- "github.com/cgrates/cgrates/engine"
- "github.com/cgrates/cgrates/utils"
- "github.com/ericlagergren/decimal"
-)
-
-// newAbstractBalance constructs an abstractBalanceOperator
-func newAbstractBalanceOperator(blnCfg *utils.Balance, cncrtBlncs []*concreteBalance,
- fltrS *engine.FilterS, ralsConns []string) balanceOperator {
- return &abstractBalance{blnCfg, cncrtBlncs, fltrS, ralsConns}
-}
-
-// abstractBalance is the operator for *abstract balance type
-type abstractBalance struct {
- blnCfg *utils.Balance
- cncrtBlncs []*concreteBalance // paying balances
- fltrS *engine.FilterS
- ralsConns []string
-}
-
-// debitUsage implements the balanceOperator interface
-func (ab *abstractBalance) debitUsage(usage *decimal.Big, startTime time.Time,
- cgrEv *utils.CGREventWithOpts) (ec *utils.EventCharges, err error) {
- //var uF *utils.UsageFactor
- if _, _, err = usageWithFactor(ab.blnCfg, ab.fltrS, cgrEv, usage); err != nil {
- return
- }
- return
-}
diff --git a/accounts/abstractbalance.go b/accounts/abstractbalance.go
new file mode 100644
index 000000000..5a3ea9111
--- /dev/null
+++ b/accounts/abstractbalance.go
@@ -0,0 +1,135 @@
+/*
+Real-time Online/Offline Charging System (OerS) for Telecom & ISP environments
+Copyright (C) ITsysCOM GmbH
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see
+*/
+
+package accounts
+
+import (
+ "fmt"
+ "time"
+
+ "github.com/cgrates/cgrates/engine"
+ "github.com/cgrates/cgrates/utils"
+ "github.com/ericlagergren/decimal"
+)
+
+// newAbstractBalance constructs an abstractBalanceOperator
+func newAbstractBalanceOperator(blnCfg *utils.Balance, cncrtBlncs []*concreteBalance,
+ fltrS *engine.FilterS, ralsConns []string) balanceOperator {
+ return &abstractBalance{blnCfg, cncrtBlncs, fltrS, ralsConns}
+}
+
+// abstractBalance is the operator for *abstract balance type
+type abstractBalance struct {
+ blnCfg *utils.Balance
+ cncrtBlncs []*concreteBalance // paying balances
+ fltrS *engine.FilterS
+ ralsConns []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.Clone() // need clone since we might modify
+ return
+ }
+ // nothing matched, return default
+ costIcrm = &utils.CostIncrement{
+ Increment: decimal.New(1, 0),
+ RecurrentFee: decimal.New(-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
+ }
+ // nothing matched, return default
+ uF = &utils.UnitFactor{
+ Factor: decimal.New(1, 0),
+ }
+ return
+}
+
+// balanceLimit returns the balance's limit
+func (aB *abstractBalance) balanceLimit() (bL *decimal.Big) {
+ if lmtIface, has := aB.blnCfg.Opts[utils.MetaBalanceLimit]; has {
+ bL = lmtIface.(*decimal.Big)
+ return
+ }
+ // nothing matched, return default
+ bL = decimal.New(0, 0)
+ return
+}
+
+// debitUsage implements the balanceOperator interface
+func (aB *abstractBalance) debitUsage(usage *decimal.Big, startTime time.Time,
+ cgrEv *utils.CGREventWithOpts) (ec *utils.EventCharges, err error) {
+
+ evNm := utils.MapStorage{
+ utils.MetaOpts: cgrEv.Opts,
+ utils.MetaReq: cgrEv.Event,
+ }
+
+ // pass the general balance filters
+ var pass bool
+ if pass, err = aB.fltrS.Pass(cgrEv.CGREvent.Tenant, aB.blnCfg.FilterIDs, evNm); err != nil {
+ return
+ } else if !pass {
+ return nil, utils.ErrFilterNotPassingNoCaps
+ }
+
+ // costIncrement
+ var costIcrm *utils.CostIncrement
+ if costIcrm, err = aB.costIncrement(cgrEv.CGREvent.Tenant, evNm); err != nil {
+ return
+ }
+
+ blcVal := new(decimal.Big).SetFloat64(aB.blnCfg.Value) // FixMe without float64
+
+ // balanceLimit
+ var hasLmt bool
+ blncLmt := aB.balanceLimit()
+ if blncLmt.Cmp(decimal.New(0, 0)) != 0 {
+ blcVal = utils.SubstractBig(blcVal, blncLmt)
+ hasLmt = true
+ }
+
+ // unitFactor
+ var uF *utils.UnitFactor
+ if uF, err = aB.unitFactor(cgrEv.CGREvent.Tenant, evNm); err != nil {
+ return
+ }
+
+ fmt.Printf("costIcrm: %+v, blncLmt: %+v, hasLmt: %+v, uF: %+v", costIcrm, blncLmt, hasLmt, uF)
+
+ return
+}
diff --git a/accounts/concretebalance.go b/accounts/concretebalance.go
index 322aba6c2..fbc67784a 100644
--- a/accounts/concretebalance.go
+++ b/accounts/concretebalance.go
@@ -42,38 +42,39 @@ type concreteBalance struct {
}
// debit implements the balanceOperator interface
-func (cb *concreteBalance) debitUsage(usage *decimal.Big, startTime time.Time,
+func (cB *concreteBalance) debitUsage(usage *decimal.Big, startTime time.Time,
cgrEv *utils.CGREventWithOpts) (ec *utils.EventCharges, err error) {
- //var uF *utils.UsageFactor
- if _, _, err = usageWithFactor(cb.blnCfg, cb.fltrS, cgrEv, usage); err != nil {
- return
- }
- return
-}
-// debitUnits is a direct debit of balance units
-func (cb *concreteBalance) debitUnits(dUnts *decimal.Big, incrm *decimal.Big,
- cgrEv *utils.CGREventWithOpts) (dbted *decimal.Big, mtchedUF *utils.UnitFactor, err error) {
- // *balanceLimit
- blncLmt := decimal.New(0, 0)
- if lmt, has := cb.blnCfg.Opts[utils.MetaBalanceLimit].(*decimal.Big); has {
- blncLmt = lmt
- }
- blcVal := new(decimal.Big).SetFloat64(cb.blnCfg.Value)
- var hasLmt bool
- if blncLmt.Cmp(decimal.New(0, 0)) != 0 {
- blcVal = utils.SubstractBig(blcVal, blncLmt)
- hasLmt = true
- }
- // dynamic unit factor
- fctr := decimal.New(1, 0)
evNm := utils.MapStorage{
utils.MetaOpts: cgrEv.Opts,
utils.MetaReq: cgrEv.Event,
}
- for _, uF := range cb.blnCfg.UnitFactors {
+
+ // pass the general balance filters
+ var pass bool
+ if pass, err = cB.fltrS.Pass(cgrEv.CGREvent.Tenant, cB.blnCfg.FilterIDs, evNm); err != nil {
+ return
+ } else if !pass {
+ return nil, utils.ErrFilterNotPassingNoCaps
+ }
+
+ return
+}
+
+// debitUnits is a direct debit of balance units
+func (cB *concreteBalance) debitUnits(dUnts *decimal.Big, incrm *decimal.Big,
+ cgrEv *utils.CGREventWithOpts) (dbted *decimal.Big, mtchedUF *utils.UnitFactor, err error) {
+
+ evNm := utils.MapStorage{
+ utils.MetaOpts: cgrEv.Opts,
+ utils.MetaReq: cgrEv.Event,
+ }
+
+ // dynamic unit factor
+ fctr := decimal.New(1, 0)
+ for _, uF := range cB.blnCfg.UnitFactors {
var pass bool
- if pass, err = cb.fltrS.Pass(cgrEv.CGREvent.Tenant, uF.FilterIDs, evNm); err != nil {
+ if pass, err = cB.fltrS.Pass(cgrEv.CGREvent.Tenant, uF.FilterIDs, evNm); err != nil {
return nil, nil, err
} else if !pass {
continue
@@ -83,11 +84,24 @@ func (cb *concreteBalance) debitUnits(dUnts *decimal.Big, incrm *decimal.Big,
break
}
var hasUF bool
- if fctr.Cmp(decimal.New(1, 0)) != 0 {
+ if mtchedUF != nil && fctr.Cmp(decimal.New(1, 0)) != 0 {
dUnts = utils.MultiplyBig(dUnts, fctr)
incrm = utils.MultiplyBig(incrm, fctr)
hasUF = true
}
+
+ blcVal := new(decimal.Big).SetFloat64(cB.blnCfg.Value) // FixMe without float64
+
+ // *balanceLimit
+ var hasLmt bool
+ blncLmt := decimal.New(0, 0)
+ if lmt, has := cB.blnCfg.Opts[utils.MetaBalanceLimit].(*decimal.Big); has {
+ blncLmt = lmt
+ }
+ if blncLmt.Cmp(decimal.New(0, 0)) != 0 {
+ blcVal = utils.SubstractBig(blcVal, blncLmt)
+ hasLmt = true
+ }
if blcVal.Cmp(dUnts) == -1 { // balance smaller than debit
maxIncrm := utils.DivideBig(blcVal, incrm).RoundToInt()
dUnts = utils.MultiplyBig(incrm, maxIncrm)
@@ -105,6 +119,6 @@ func (cb *concreteBalance) debitUnits(dUnts *decimal.Big, incrm *decimal.Big,
if !ok {
return nil, nil, fmt.Errorf("failed representing decimal <%s> as float64", rmain)
}
- cb.blnCfg.Value = rmainFlt64
+ cB.blnCfg.Value = rmainFlt64
return
}
diff --git a/accounts/libaccounts_test.go b/accounts/concretebalance_test.go
similarity index 86%
rename from accounts/libaccounts_test.go
rename to accounts/concretebalance_test.go
index 1073d3677..ba924e524 100644
--- a/accounts/libaccounts_test.go
+++ b/accounts/concretebalance_test.go
@@ -59,22 +59,24 @@ func TestCBDebitUnits(t *testing.T) {
//with increment and not enough balance
cb = &concreteBalance{
blnCfg: &utils.Balance{
- ID: "TestCBDebitUnits",
- Type: utils.MetaConcrete,
+ ID: "TestCBDebitUnits",
+ Type: utils.MetaConcrete,
+ Opts: map[string]interface{}{
+ utils.MetaBalanceLimit: decimal.New(-1, 0),
+ },
Value: 1.25,
},
fltrS: new(engine.FilterS),
}
+ remBlnc, _ := new(decimal.Big).SetString("2.2")
if dbted, _, err := cb.debitUnits(
- new(decimal.Big).SetFloat64(1.5),
+ new(decimal.Big).SetFloat64(2.5),
new(decimal.Big).SetFloat64(0.1),
&utils.CGREventWithOpts{CGREvent: &utils.CGREvent{Tenant: "cgrates.org"}}); err != nil {
t.Error(err)
- // #FixMe: new(decimal.Big).SetFloat64(1.2) does not work :(
- } else if dbted.Cmp(decimal.New(12, 1)) != 0 { // only 1.2 is possible due to increment
- //} else if dbted.Cmp(new(decimal.Big).SetFloat64(1.2)) != 0 {
+ } else if dbted.Cmp(remBlnc) != 0 { // only 1.2 is possible due to increment
t.Errorf("debited: %s, cmp: %v", dbted, dbted.Cmp(new(decimal.Big).SetFloat64(1.2)))
- } else if cb.blnCfg.Value != 0.05 {
+ } else if cb.blnCfg.Value != -0.95 {
t.Errorf("balance remaining: %f", cb.blnCfg.Value)
}
}
diff --git a/accounts/libaccounts.go b/accounts/libaccounts.go
index 2ccaa6137..7091c0bae 100644
--- a/accounts/libaccounts.go
+++ b/accounts/libaccounts.go
@@ -86,44 +86,3 @@ type balanceOperator interface {
debitUsage(usage *decimal.Big, startTime time.Time,
cgrEv *utils.CGREventWithOpts) (ec *utils.EventCharges, err error)
}
-
-// usagewithFactor returns the usage considering also factor for the debit
-// includes event filtering to avoid code duplication
-func usageWithFactor(blnCfg *utils.Balance, fltrS *engine.FilterS,
- cgrEv *utils.CGREventWithOpts, usage *decimal.Big) (resUsage *decimal.Big, mtchedUF *utils.UnitFactor, err error) {
- resUsage = usage
- fctr := decimal.New(1, 0)
- if len(blnCfg.FilterIDs) == 0 &&
- len(blnCfg.UnitFactors) == 0 {
- return
- }
- evNm := utils.MapStorage{
- utils.MetaOpts: cgrEv.Opts,
- utils.MetaReq: cgrEv.Event,
- }
- // match the general balance filters
- var pass bool
- if pass, err = fltrS.Pass(cgrEv.CGREvent.Tenant, blnCfg.FilterIDs, evNm); err != nil {
- return nil, nil, err
- } else if !pass {
- return nil, nil, utils.ErrFilterNotPassingNoCaps
- }
- // find out the factor
- for _, uF := range blnCfg.UnitFactors {
- if pass, err = fltrS.Pass(cgrEv.CGREvent.Tenant, uF.FilterIDs, evNm); err != nil {
- return nil, nil, err
- } else if !pass {
- continue
- }
- fctr = uF.Factor
- mtchedUF = uF
- break
- }
- if mtchedUF == nil {
- return
- }
- if fctr.Cmp(decimal.New(1, 0)) != 0 {
- resUsage = utils.MultiplyBig(usage, fctr)
- }
- return
-}
diff --git a/config/accountscfg.go b/config/accountscfg.go
index 5474635a9..22e71e620 100644
--- a/config/accountscfg.go
+++ b/config/accountscfg.go
@@ -23,6 +23,7 @@ import "github.com/cgrates/cgrates/utils"
// AccountSCfg is the configuration of ActionS
type AccountSCfg struct {
Enabled bool
+ AttributeSConns []string
RateSConns []string
ThresholdSConns []string
IndexedSelects bool
diff --git a/utils/accountprofile.go b/utils/accountprofile.go
index abe81354b..e8f960ac0 100644
--- a/utils/accountprofile.go
+++ b/utils/accountprofile.go
@@ -39,14 +39,52 @@ type AccountProfile struct {
// Balance represents one Balance inside an Account
type Balance struct {
- ID string // Balance identificator, unique within an Account
- FilterIDs []string
- Weight float64
- Blocker bool
- Type string
- Opts map[string]interface{}
- UnitFactors []*UnitFactor
- Value float64
+ ID string // Balance identificator, unique within an Account
+ FilterIDs []string
+ Weight float64
+ Blocker bool
+ Type string
+ Opts map[string]interface{}
+ CostIncrements []*CostIncrement
+ CostAttributes []*CostAttributes
+ UnitFactors []*UnitFactor
+ Value float64
+}
+
+// CostAttributes will attach attribute profiles to cost events
+type CostAttributes struct {
+ FilterIDs []string
+ AttributeProfileIDs []string
+}
+
+// CostIncrement enforces cost calculation to specific balance increments
+type CostIncrement struct {
+ FilterIDs []string
+ Increment *decimal.Big
+ FixedFee *decimal.Big
+ RecurrentFee *decimal.Big
+}
+
+// Clone returns a copy of the CostIncrement
+func (cI *CostIncrement) Clone() (cIcln *CostIncrement) {
+ cIcln = new(CostIncrement)
+ if cI.FilterIDs != nil {
+ cIcln.FilterIDs = make([]string, len(cI.FilterIDs))
+ for i, fID := range cI.FilterIDs {
+ cIcln.FilterIDs[i] = fID
+ }
+ }
+ if cI.Increment != nil {
+ cIcln.Increment = new(decimal.Big).Copy(cI.Increment)
+ }
+ if cI.FixedFee != nil {
+ cIcln.FixedFee = new(decimal.Big).Copy(cI.FixedFee)
+ }
+ if cI.RecurrentFee != nil {
+ cIcln.RecurrentFee = new(decimal.Big).Copy(cI.RecurrentFee)
+ }
+ return
+
}
// UnitFactor is a multiplicator for the usage received
diff --git a/utils/chrgdaccount.go b/utils/chrgdaccount.go
deleted file mode 100644
index 4dd717d46..000000000
--- a/utils/chrgdaccount.go
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
-Real-time Online/Offline Charging System (OerS) for Telecom & ISP environments
-Copyright (C) ITsysCOM GmbH
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see
-*/
-
-package utils
-
-type ChargedAccount struct{} // placeholder for now
diff --git a/utils/eventcharges.go b/utils/eventcharges.go
index 3e8f84bc0..06cd18742 100644
--- a/utils/eventcharges.go
+++ b/utils/eventcharges.go
@@ -18,13 +18,17 @@ along with this program. If not, see
package utils
-import "time"
+import (
+ "time"
+
+ "github.com/ericlagergren/decimal"
+)
// EventCharges records the charges applied to an Event
type EventCharges struct {
StartTime *time.Time
- Usage *time.Duration
- Cost *float64
+ Usage *decimal.Big
+ Cost *decimal.Big
Charges []*ChargedInterval
Account *AccountProfile
Accounting *ChargedAccounting