From 9c32b2310a50ec0001d0031cf690984169e7cc23 Mon Sep 17 00:00:00 2001 From: TeoV Date: Thu, 7 Jan 2021 14:32:49 +0200 Subject: [PATCH] Add APIAccountProfile struct + commands in cgr-console --- accounts/concretebalance.go | 4 +- accounts/concretebalance_test.go | 16 ++-- apier/v1/accountprofiles.go | 19 ++-- apier/v1/accountprofiles_it_test.go | 76 ++++++++------- console/accounts_profile.go | 65 +++++++++++++ console/accounts_profile_ids.go | 65 +++++++++++++ console/accounts_profile_rem.go | 62 +++++++++++++ console/accounts_profile_set.go | 65 +++++++++++++ engine/model_helpers.go | 7 +- engine/model_helpers_test.go | 6 +- utils/accountprofile.go | 139 +++++++++++++++++++++++++++- utils/accountprofile_test.go | 4 +- 12 files changed, 466 insertions(+), 62 deletions(-) create mode 100644 console/accounts_profile.go create mode 100644 console/accounts_profile_ids.go create mode 100644 console/accounts_profile_rem.go create mode 100644 console/accounts_profile_set.go diff --git a/accounts/concretebalance.go b/accounts/concretebalance.go index 8a7577d64..8566529ab 100644 --- a/accounts/concretebalance.go +++ b/accounts/concretebalance.go @@ -138,7 +138,7 @@ func (cB *concreteBalance) debitUnits(dUnts *decimal.Big, incrm *decimal.Big, hasUF = true } - blcVal := new(decimal.Big).SetFloat64(cB.blnCfg.Units) // FixMe without float64 + blcVal := cB.blnCfg.Units.Big // balanceLimit var hasLmt bool @@ -166,6 +166,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.Units = rmainFlt64 + cB.blnCfg.Units, err = utils.NewDecimalFromFloat64(rmainFlt64) return } diff --git a/accounts/concretebalance_test.go b/accounts/concretebalance_test.go index 5107bd583..13bfae8e2 100644 --- a/accounts/concretebalance_test.go +++ b/accounts/concretebalance_test.go @@ -41,7 +41,7 @@ func TestCBDebitUnits(t *testing.T) { Factor: &utils.Decimal{decimal.New(100, 0)}, // EuroCents }, }, - Units: 500, // 500 EURcents + Units: &utils.Decimal{decimal.New(500, 0)}, // 500 EURcents }, fltrS: new(engine.FilterS), } @@ -53,7 +53,7 @@ func TestCBDebitUnits(t *testing.T) { t.Errorf("received unit factor: %+v", uFctr) } else if dbted.Cmp(toDebit) != 0 { t.Errorf("debited: %s", dbted) - } else if cb.blnCfg.Units != -100.0 { + } else if cb.blnCfg.Units.String() != "-100" { t.Errorf("balance remaining: %f", cb.blnCfg.Units) } //with increment and not enough balance @@ -64,7 +64,7 @@ func TestCBDebitUnits(t *testing.T) { Opts: map[string]interface{}{ utils.MetaBalanceLimit: decimal.New(-1, 0), }, - Units: 1.25, + Units: &utils.Decimal{decimal.New(125, 2)}, }, fltrS: new(engine.FilterS), } @@ -75,7 +75,7 @@ func TestCBDebitUnits(t *testing.T) { t.Error(err) } else if dbted.Cmp(decimal.New(22, 1)) != 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.Units != -0.95 { + } else if cb.blnCfg.Units.String() != "-0.95" { t.Errorf("balance remaining: %f", cb.blnCfg.Units) } //with increment and unlimited balance @@ -86,7 +86,7 @@ func TestCBDebitUnits(t *testing.T) { Opts: map[string]interface{}{ utils.MetaBalanceUnlimited: true, }, - Units: 1.25, + Units: &utils.Decimal{decimal.New(125, 2)}, }, fltrS: new(engine.FilterS), } @@ -97,7 +97,7 @@ func TestCBDebitUnits(t *testing.T) { t.Error(err) } else if dbted.Cmp(decimal.New(25, 1)) != 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.Units != -1.25 { + } else if cb.blnCfg.Units.String() != "-1.25" { t.Errorf("balance remaining: %f", cb.blnCfg.Units) } //with increment and positive limit @@ -108,7 +108,7 @@ func TestCBDebitUnits(t *testing.T) { Opts: map[string]interface{}{ utils.MetaBalanceLimit: decimal.New(5, 1), // 0.5 as limit }, - Units: 1.25, + Units: &utils.Decimal{decimal.New(125, 2)}, }, fltrS: new(engine.FilterS), } @@ -119,7 +119,7 @@ func TestCBDebitUnits(t *testing.T) { t.Error(err) } else if dbted.Cmp(decimal.New(7, 1)) != 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.Units != 0.55 { + } else if cb.blnCfg.Units.String() != "0.55" { t.Errorf("balance remaining: %f", cb.blnCfg.Units) } } diff --git a/apier/v1/accountprofiles.go b/apier/v1/accountprofiles.go index 941ba1e2f..30fb23e4f 100644 --- a/apier/v1/accountprofiles.go +++ b/apier/v1/accountprofiles.go @@ -87,21 +87,24 @@ func (apierSv1 *APIerSv1) GetAccountProfileIDsCount(args *utils.TenantWithOpts, return } -type AccountProfileWithCache struct { - *utils.AccountProfileWithOpts +type APIAccountProfileWithCache struct { + *utils.APIAccountProfileWithOpts Cache *string } //SetAccountProfile add/update a new Account Profile -func (apierSv1 *APIerSv1) SetAccountProfile(ap *AccountProfileWithCache, reply *string) error { - if missing := utils.MissingStructFields(ap.AccountProfile, []string{utils.ID}); len(missing) != 0 { +func (apierSv1 *APIerSv1) SetAccountProfile(extAp *APIAccountProfileWithCache, reply *string) error { + if missing := utils.MissingStructFields(extAp.APIAccountProfile, []string{utils.ID}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } - if ap.Tenant == utils.EmptyString { - ap.Tenant = apierSv1.Config.GeneralCfg().DefaultTenant + if extAp.Tenant == utils.EmptyString { + extAp.Tenant = apierSv1.Config.GeneralCfg().DefaultTenant } - - if err := apierSv1.DataManager.SetAccountProfile(ap.AccountProfile, true); err != nil { + ap, err := extAp.AsAccountProfile() + if err != nil { + return err + } + if err := apierSv1.DataManager.SetAccountProfile(ap, true); err != nil { return utils.APIErrorHandler(err) } //generate a loadID for CacheAccountProfiles and store it in database diff --git a/apier/v1/accountprofiles_it_test.go b/apier/v1/accountprofiles_it_test.go index aceb5b505..acb493bce 100644 --- a/apier/v1/accountprofiles_it_test.go +++ b/apier/v1/accountprofiles_it_test.go @@ -39,7 +39,8 @@ var ( accPrfCfg *config.CGRConfig accSRPC *rpc.Client accPrfDataDir = "/usr/share/cgrates" - accPrf *AccountProfileWithCache + apiAccPrf *APIAccountProfileWithCache + accPrf *utils.AccountProfile accPrfConfigDIR string //run tests for specific configuration sTestsAccPrf = []func(t *testing.T){ @@ -157,7 +158,7 @@ func testAccountSGetAccountProfile(t *testing.T) { Factor: &utils.Decimal{decimal.New(200, 0)}, }, }, - Units: 14, + Units: &utils.Decimal{decimal.New(14, 0)}, }, &utils.Balance{ ID: "VoiceBalance", @@ -167,7 +168,7 @@ func testAccountSGetAccountProfile(t *testing.T) { CostIncrements: []*utils.CostIncrement{}, CostAttributes: []string{}, UnitFactors: []*utils.UnitFactor{}, - Units: 3600000000000, + Units: &utils.Decimal{decimal.New(3600000000000, 0)}, }, }, ThresholdIDs: []string{utils.META_NONE}, @@ -197,48 +198,45 @@ func testAccountSPing(t *testing.T) { } func testAccountSSettAccountProfile(t *testing.T) { - accPrf = &AccountProfileWithCache{ - AccountProfileWithOpts: &utils.AccountProfileWithOpts{ - AccountProfile: &utils.AccountProfile{ + apiAccPrf = &APIAccountProfileWithCache{ + APIAccountProfileWithOpts: &utils.APIAccountProfileWithOpts{ + APIAccountProfile: &utils.APIAccountProfile{ Tenant: "cgrates.org", ID: "id_test", Weight: 10, - Balances: []*utils.Balance{ - &utils.Balance{ + Balances: []*utils.APIBalance{ + &utils.APIBalance{ ID: "MonetaryBalance", FilterIDs: []string{}, Weight: 10, Type: utils.MONETARY, - CostIncrements: []*utils.CostIncrement{ - &utils.CostIncrement{ + CostIncrements: []*utils.APICostIncrement{ + &utils.APICostIncrement{ FilterIDs: []string{"fltr1", "fltr2"}, - Increment: &utils.Decimal{decimal.New(13, 1)}, - FixedFee: &utils.Decimal{decimal.New(23, 1)}, - RecurrentFee: &utils.Decimal{decimal.New(33, 1)}, + Increment: utils.Float64Pointer(1.3), + FixedFee: utils.Float64Pointer(2.3), + RecurrentFee: utils.Float64Pointer(3.3), }, }, CostAttributes: []string{"attr1", "attr2"}, - UnitFactors: []*utils.UnitFactor{ - &utils.UnitFactor{ + UnitFactors: []*utils.APIUnitFactor{ + &utils.APIUnitFactor{ FilterIDs: []string{"fltr1", "fltr2"}, - Factor: &utils.Decimal{decimal.New(100, 0)}, + Factor: 100, }, - &utils.UnitFactor{ + &utils.APIUnitFactor{ FilterIDs: []string{"fltr3"}, - Factor: &utils.Decimal{decimal.New(200, 0)}, + Factor: 200, }, }, Units: 14, }, - &utils.Balance{ - ID: "VoiceBalance", - FilterIDs: []string{}, - Weight: 10, - Type: utils.VOICE, - CostIncrements: []*utils.CostIncrement{}, - CostAttributes: []string{}, - UnitFactors: []*utils.UnitFactor{}, - Units: 3600000000000, + &utils.APIBalance{ + ID: "VoiceBalance", + FilterIDs: []string{}, + Weight: 10, + Type: utils.VOICE, + Units: 3600000000000, }, }, ThresholdIDs: []string{utils.META_NONE}, @@ -253,17 +251,21 @@ func testAccountSSettAccountProfile(t *testing.T) { t.Errorf("Expected error: %v received: %v", expErr, err) } var reply string - if err := accSRPC.Call(utils.APIerSv1SetAccountProfile, accPrf, &reply); err != nil { + if err := accSRPC.Call(utils.APIerSv1SetAccountProfile, apiAccPrf, &reply); err != nil { t.Error(err) } else if reply != utils.OK { t.Error("Unexpected reply returned", reply) } + var err error + if accPrf, err = apiAccPrf.AsAccountProfile(); err != nil { + t.Error(err) + } var reply2 *utils.AccountProfile if err := accSRPC.Call(utils.APIerSv1GetAccountProfile, &utils.TenantIDWithOpts{ TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "id_test"}}, &reply2); err != nil { t.Error(err) - } else if !reflect.DeepEqual(accPrf.AccountProfile, reply2) { - t.Errorf("Expecting : %+v, received: %+v", accPrf.AccountProfile, reply2) + } else if !reflect.DeepEqual(accPrf, reply2) { + t.Errorf("Expecting : %+v, received: %+v", accPrf, reply2) } } @@ -305,19 +307,23 @@ func testAccountSGetAccountProfileIDsCount(t *testing.T) { func testAccountSUpdateAccountProfile(t *testing.T) { var reply string - accPrf.Weight = 2 - accPrf.Balances[0].CostIncrements[0].FixedFee = &utils.Decimal{decimal.New(1234, 1)} - if err := accSRPC.Call(utils.APIerSv1SetAccountProfile, accPrf, &reply); err != nil { + apiAccPrf.Weight = 2 + apiAccPrf.Balances[0].CostIncrements[0].FixedFee = utils.Float64Pointer(123.5) + if err := accSRPC.Call(utils.APIerSv1SetAccountProfile, apiAccPrf, &reply); err != nil { t.Error(err) } else if reply != utils.OK { t.Error("Unexpected reply returned", reply) } + var err error + if accPrf, err = apiAccPrf.AsAccountProfile(); err != nil { + t.Error(err) + } var reply2 *utils.AccountProfile if err := accSRPC.Call(utils.APIerSv1GetAccountProfile, &utils.TenantIDWithOpts{ TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "id_test"}}, &reply2); err != nil { t.Error(err) - } else if !reflect.DeepEqual(accPrf.AccountProfile, reply2) { - t.Errorf("Expecting : %+v, received: %+v", accPrf.AccountProfile, reply2) + } else if !reflect.DeepEqual(accPrf, reply2) { + t.Errorf("Expecting : %+v, received: %+v", accPrf, reply2) } } diff --git a/console/accounts_profile.go b/console/accounts_profile.go new file mode 100644 index 000000000..6b96e885a --- /dev/null +++ b/console/accounts_profile.go @@ -0,0 +1,65 @@ +/* +Real-time Online/Offline Charging System (OCS) 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 console + +import ( + "github.com/cgrates/cgrates/utils" +) + +func init() { + c := &CmdGetAccountsProfile{ + name: "accounts_profile", + rpcMethod: utils.APIerSv1GetAccountProfile, + rpcParams: &utils.TenantID{}, + } + commands[c.Name()] = c + c.CommandExecuter = &CommandExecuter{c} +} + +// Commander implementation +type CmdGetAccountsProfile struct { + name string + rpcMethod string + rpcParams *utils.TenantID + *CommandExecuter +} + +func (self *CmdGetAccountsProfile) Name() string { + return self.name +} + +func (self *CmdGetAccountsProfile) RpcMethod() string { + return self.rpcMethod +} + +func (self *CmdGetAccountsProfile) RpcParams(reset bool) interface{} { + if reset || self.rpcParams == nil { + self.rpcParams = &utils.TenantID{} + } + return self.rpcParams +} + +func (self *CmdGetAccountsProfile) PostprocessRpcParams() error { + return nil +} + +func (self *CmdGetAccountsProfile) RpcResult() interface{} { + var atr utils.AccountProfile + return &atr +} diff --git a/console/accounts_profile_ids.go b/console/accounts_profile_ids.go new file mode 100644 index 000000000..658cd3e67 --- /dev/null +++ b/console/accounts_profile_ids.go @@ -0,0 +1,65 @@ +/* +Real-time Online/Offline Charging System (OCS) 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 console + +import ( + "github.com/cgrates/cgrates/utils" +) + +func init() { + c := &CmdGetAccountsProfileIDs{ + name: "accounts_profile_ids", + rpcMethod: utils.APIerSv1GetAccountProfileIDs, + rpcParams: &utils.PaginatorWithTenant{}, + } + commands[c.Name()] = c + c.CommandExecuter = &CommandExecuter{c} +} + +// Commander implementation +type CmdGetAccountsProfileIDs struct { + name string + rpcMethod string + rpcParams *utils.PaginatorWithTenant + *CommandExecuter +} + +func (self *CmdGetAccountsProfileIDs) Name() string { + return self.name +} + +func (self *CmdGetAccountsProfileIDs) RpcMethod() string { + return self.rpcMethod +} + +func (self *CmdGetAccountsProfileIDs) RpcParams(reset bool) interface{} { + if reset || self.rpcParams == nil { + self.rpcParams = &utils.PaginatorWithTenant{} + } + return self.rpcParams +} + +func (self *CmdGetAccountsProfileIDs) PostprocessRpcParams() error { + return nil +} + +func (self *CmdGetAccountsProfileIDs) RpcResult() interface{} { + var atr []string + return &atr +} diff --git a/console/accounts_profile_rem.go b/console/accounts_profile_rem.go new file mode 100644 index 000000000..05dbb4e1e --- /dev/null +++ b/console/accounts_profile_rem.go @@ -0,0 +1,62 @@ +/* +Real-time Online/Offline Charging System (OCS) 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 console + +import "github.com/cgrates/cgrates/utils" + +func init() { + c := &CmdRemoveAccountsProfile{ + name: "accounts_profile_remove", + rpcMethod: utils.APIerSv1RemoveAccountProfile, + rpcParams: &utils.TenantIDWithCache{}, + } + commands[c.Name()] = c + c.CommandExecuter = &CommandExecuter{c} +} + +type CmdRemoveAccountsProfile struct { + name string + rpcMethod string + rpcParams *utils.TenantIDWithCache + *CommandExecuter +} + +func (self *CmdRemoveAccountsProfile) Name() string { + return self.name +} + +func (self *CmdRemoveAccountsProfile) RpcMethod() string { + return self.rpcMethod +} + +func (self *CmdRemoveAccountsProfile) RpcParams(reset bool) interface{} { + if reset || self.rpcParams == nil { + self.rpcParams = &utils.TenantIDWithCache{Opts: make(map[string]interface{})} + } + return self.rpcParams +} + +func (self *CmdRemoveAccountsProfile) PostprocessRpcParams() error { + return nil +} + +func (self *CmdRemoveAccountsProfile) RpcResult() interface{} { + var s string + return &s +} diff --git a/console/accounts_profile_set.go b/console/accounts_profile_set.go new file mode 100644 index 000000000..9b605a8a3 --- /dev/null +++ b/console/accounts_profile_set.go @@ -0,0 +1,65 @@ +/* +Real-time Online/Offline Charging System (OCS) 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 console + +import ( + v1 "github.com/cgrates/cgrates/apier/v1" + "github.com/cgrates/cgrates/utils" +) + +func init() { + c := &CmdSetAccountProfile{ + name: "accounts_profile_set", + rpcMethod: utils.APIerSv1SetAccountProfile, + rpcParams: &v1.APIAccountProfileWithCache{}, + } + commands[c.Name()] = c + c.CommandExecuter = &CommandExecuter{c} +} + +type CmdSetAccountProfile struct { + name string + rpcMethod string + rpcParams *v1.APIAccountProfileWithCache + *CommandExecuter +} + +func (self *CmdSetAccountProfile) Name() string { + return self.name +} + +func (self *CmdSetAccountProfile) RpcMethod() string { + return self.rpcMethod +} + +func (self *CmdSetAccountProfile) RpcParams(reset bool) interface{} { + if reset || self.rpcParams == nil { + self.rpcParams = &v1.APIAccountProfileWithCache{APIAccountProfileWithOpts: new(utils.APIAccountProfileWithOpts)} + } + return self.rpcParams +} + +func (self *CmdSetAccountProfile) PostprocessRpcParams() error { + return nil +} + +func (self *CmdSetAccountProfile) RpcResult() interface{} { + var s string + return &s +} diff --git a/engine/model_helpers.go b/engine/model_helpers.go index 1a87f7157..3403e8f8c 100644 --- a/engine/model_helpers.go +++ b/engine/model_helpers.go @@ -3726,7 +3726,9 @@ func APItoAccountProfile(tpAp *utils.TPAccountProfile, timezone string) (ap *uti Weight: bal.Weight, Blocker: bal.Blocker, Type: bal.Type, - Units: bal.Units, + } + if ap.Balances[i].Units, err = utils.NewDecimalFromFloat64(bal.Units); err != nil { + return } if bal.Opts != utils.EmptyString { ap.Balances[i].Opts = make(map[string]interface{}) @@ -3815,9 +3817,8 @@ func AccountProfileToAPI(ap *utils.AccountProfile) (tpAp *utils.TPAccountProfile Weight: bal.Weight, Blocker: bal.Blocker, Type: bal.Type, - Units: bal.Units, } - + tpAp.Balances[i].Units, _ = bal.Units.Float64() elems := make([]string, 0, len(bal.Opts)) for k, v := range bal.Opts { elems = append(elems, utils.ConcatenatedKey(k, utils.IfaceAsString(v))) diff --git a/engine/model_helpers_test.go b/engine/model_helpers_test.go index fcf4ed0a5..dc2102b9c 100644 --- a/engine/model_helpers_test.go +++ b/engine/model_helpers_test.go @@ -25,6 +25,8 @@ import ( "testing" "time" + "github.com/ericlagergren/decimal" + "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/utils" ) @@ -7230,7 +7232,7 @@ func TestApitoAccountProfileCase2(t *testing.T) { FilterIDs: []string{"FLTR_RES_GR2"}, Weight: 10, Type: utils.VOICE, - Units: 3600000000000, + Units: &utils.Decimal{decimal.New(3600000000000, 0)}, Opts: map[string]interface{}{ "key1": "val1", }, @@ -7388,7 +7390,7 @@ func TestModelHelpersAccountProfileToAPI(t *testing.T) { FilterIDs: []string{"FLTR_RES_GR2"}, Weight: 10, Type: utils.VOICE, - Units: 3600000000000, + Units: &utils.Decimal{decimal.New(3600000000000, 0)}, Opts: map[string]interface{}{ "key1": "val1", }, diff --git a/utils/accountprofile.go b/utils/accountprofile.go index e7f3b94b8..9d17428ef 100644 --- a/utils/accountprofile.go +++ b/utils/accountprofile.go @@ -46,7 +46,7 @@ type Balance struct { CostIncrements []*CostIncrement CostAttributes []string UnitFactors []*UnitFactor - Units float64 + Units *Decimal } // CostIncrement enforces cost calculation to specific balance increments @@ -152,7 +152,6 @@ func (bL *Balance) Clone() (blnc *Balance) { Blocker: bL.Blocker, Type: bL.Type, Opts: make(map[string]interface{}), - Units: bL.Units, } if bL.FilterIDs != nil { blnc.FilterIDs = make([]string, len(bL.FilterIDs)) @@ -181,6 +180,9 @@ func (bL *Balance) Clone() (blnc *Balance) { blnc.UnitFactors[i] = value.Clone() } } + if bL.Units != nil { + blnc.Units = new(Decimal).Copy(bL.Units) + } return } @@ -200,6 +202,12 @@ func (blcs Balances) Sort() { sort.Slice(blcs, func(i, j int) bool { return blcs[i].Weight > blcs[j].Weight }) } +// APIAccountProfileWithOpts is used in API calls +type APIAccountProfileWithOpts struct { + *APIAccountProfile + Opts map[string]interface{} +} + // AccountProfileWithOpts is used in API calls type AccountProfileWithOpts struct { *AccountProfile @@ -217,3 +225,130 @@ type ReplyMaxUsage struct { MaxUsage time.Duration Cost *EventCharges } + +// APIAccountProfile represents one APIAccount on a Tenant +type APIAccountProfile struct { + Tenant string + ID string + FilterIDs []string + ActivationInterval *ActivationInterval + Weight float64 + Opts map[string]interface{} + Balances []*APIBalance + ThresholdIDs []string +} + +func (ext *APIAccountProfile) AsAccountProfile() (profile *AccountProfile, err error) { + profile = &AccountProfile{ + Tenant: ext.Tenant, + ID: ext.ID, + FilterIDs: ext.FilterIDs, + ActivationInterval: ext.ActivationInterval, + Weight: ext.Weight, + Opts: ext.Opts, + ThresholdIDs: ext.ThresholdIDs, + } + if len(ext.Balances) != 0 { + profile.Balances = make([]*Balance, len(ext.Balances)) + for i, bal := range ext.Balances { + if profile.Balances[i], err = bal.AsBalance(); err != nil { + return + } + } + } + return +} + +// APIBalance represents one APIBalance inside an APIAccount +type APIBalance struct { + ID string // Balance identificator, unique within an Account + FilterIDs []string + Weight float64 + Blocker bool + Type string + Opts map[string]interface{} + CostIncrements []*APICostIncrement + CostAttributes []string + UnitFactors []*APIUnitFactor + Units float64 +} + +func (ext *APIBalance) AsBalance() (balance *Balance, err error) { + balance = &Balance{ + ID: ext.ID, + FilterIDs: ext.FilterIDs, + Weight: ext.Weight, + Blocker: ext.Blocker, + Type: ext.Type, + Opts: ext.Opts, + CostAttributes: ext.CostAttributes, + } + if len(ext.CostIncrements) != 0 { + balance.CostIncrements = make([]*CostIncrement, len(ext.CostIncrements)) + for i, cIncr := range ext.CostIncrements { + if balance.CostIncrements[i], err = cIncr.AsCostIncrement(); err != nil { + return + } + } + } + if len(ext.UnitFactors) != 0 { + balance.UnitFactors = make([]*UnitFactor, len(ext.UnitFactors)) + for i, uFct := range ext.UnitFactors { + if balance.UnitFactors[i], err = uFct.AsUnitFactor(); err != nil { + return + } + } + } + if balance.Units, err = NewDecimalFromFloat64(ext.Units); err != nil { + return + } + return + +} + +// APICostIncrement represent one CostIncrement inside an APIBalance +type APICostIncrement struct { + FilterIDs []string + Increment *float64 + FixedFee *float64 + RecurrentFee *float64 +} + +func (ext *APICostIncrement) AsCostIncrement() (cIncr *CostIncrement, err error) { + cIncr = &CostIncrement{ + FilterIDs: ext.FilterIDs, + } + if ext.Increment != nil { + if cIncr.Increment, err = NewDecimalFromFloat64(*ext.Increment); err != nil { + return + } + } + if ext.FixedFee != nil { + if cIncr.FixedFee, err = NewDecimalFromFloat64(*ext.FixedFee); err != nil { + return + } + } + if ext.RecurrentFee != nil { + if cIncr.RecurrentFee, err = NewDecimalFromFloat64(*ext.RecurrentFee); err != nil { + return + } + } + return +} + +// APIUnitFactor represent one UnitFactor inside an APIBalance +type APIUnitFactor struct { + FilterIDs []string + Factor float64 +} + +func (ext *APIUnitFactor) AsUnitFactor() (uFac *UnitFactor, err error) { + uFac = &UnitFactor{ + FilterIDs: ext.FilterIDs, + } + if uFac.Factor, err = NewDecimalFromFloat64(ext.Factor); err != nil { + return + } + return + +} diff --git a/utils/accountprofile_test.go b/utils/accountprofile_test.go index 55ac78845..ccf439068 100644 --- a/utils/accountprofile_test.go +++ b/utils/accountprofile_test.go @@ -51,7 +51,7 @@ func TestCloneBalance(t *testing.T) { Factor: &Decimal{decimal.New(20, 2)}, }, }, - Units: 1.25, + Units: &Decimal{decimal.New(125, 3)}, } if rcv := expBlc.Clone(); !reflect.DeepEqual(rcv, expBlc) { t.Errorf("Expected %+v \n, received %+v", ToJSON(expBlc), ToJSON(rcv)) @@ -96,7 +96,7 @@ func TestCloneAccountProfile(t *testing.T) { Factor: &Decimal{decimal.New(20, 2)}, }, }, - Units: 1.25, + Units: &Decimal{decimal.New(125, 3)}, }, }, ThresholdIDs: []string{"*none"},