diff --git a/data/storage/mysql/create_tariffplan_tables.sql b/data/storage/mysql/create_tariffplan_tables.sql index 82dbf4285..cd4ba0437 100644 --- a/data/storage/mysql/create_tariffplan_tables.sql +++ b/data/storage/mysql/create_tariffplan_tables.sql @@ -553,7 +553,10 @@ CREATE TABLE tp_account_profiles ( `balance_blocker` BOOLEAN NOT NULL, `balance_type` varchar(64) NOT NULL, `balance_opts` varchar(64) NOT NULL, - `balance_value` decimal(8,2) NOT NULL, + `balance_cost_increments` varchar(64) NOT NULL, + `balance_cost_attributes` varchar(64) NOT NULL, + `balance_unit_factors` varchar(64) NOT NULL, + `balance_value` decimal(16,4) NOT NULL, `threshold_ids` varchar(64) NOT NULL, `created_at` TIMESTAMP, PRIMARY KEY (`pk`), diff --git a/data/storage/postgres/create_tariffplan_tables.sql b/data/storage/postgres/create_tariffplan_tables.sql index 04fa5eb56..da4c51581 100644 --- a/data/storage/postgres/create_tariffplan_tables.sql +++ b/data/storage/postgres/create_tariffplan_tables.sql @@ -537,7 +537,10 @@ CREATE TABLE tp_account_profiles ( "balance_blocker" BOOLEAN NOT NULL, "balance_type" varchar(64) NOT NULL, "balance_opts" varchar(64) NOT NULL, - "balance_value" decimal(8,2) NOT NULL, + "balance_cost_increments" varchar(64) NOT NULL, + "balance_cost_attributes" varchar(64) NOT NULL, + "balance_unit_factors" varchar(64) NOT NULL, + "balance_value" decimal(16,4) NOT NULL, "threshold_ids" varchar(64) NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE ); diff --git a/data/tariffplans/tutaccounts/AccountProfiles.csv b/data/tariffplans/tutaccounts/AccountProfiles.csv index df4b0d4a2..3b98128e9 100644 --- a/data/tariffplans/tutaccounts/AccountProfiles.csv +++ b/data/tariffplans/tutaccounts/AccountProfiles.csv @@ -1,3 +1,3 @@ -#Tenant,ID,FilterIDs,ActivationInterval,Weight,BalanceID,BalanceFilterIDs,BalanceWeight,BalanceBlocker,BalanceType,BalanceOpts,BalanceValue,ThresholdIDs -cgrates.org,1001,,,20,MonetaryBalance,,10,,*monetary,,14,*none -cgrates.org,1001,,,,VoiceBalance,,10,,*voice,,3600000000000, \ No newline at end of file +#Tenant,ID,FilterIDs,ActivationInterval,Weight,BalanceID,BalanceFilterIDs,BalanceWeight,BalanceBlocker,BalanceType,BalanceOpts,BalanceCostIncrements,BalanceCostAttributes,BalanceUnitFactors,BalanceValue,ThresholdIDs +cgrates.org,1001,,,20,MonetaryBalance,,10,,*monetary,,fltr1&fltr2;1.3;2.3;3.3,attr1;attr2,fltr1&fltr2;100;fltr3;200,14,*none +cgrates.org,1001,,,,VoiceBalance,,10,,*voice,,,,,3600000000000, \ No newline at end of file diff --git a/engine/libtest.go b/engine/libtest.go index d5a52febf..30d430a6a 100644 --- a/engine/libtest.go +++ b/engine/libtest.go @@ -298,9 +298,9 @@ cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP_TEST_VOICE,,false,0s,*topup,,~*balance.Test ` AccountProfileCSVContent = ` -#Tenant,ID,FilterIDs,ActivationInterval,Weight,BalanceID,BalanceFilterIDs,BalanceWeight,BalanceBlocker,BalanceType,BalanceOpts,BalanceValue,ThresholdIDs -cgrates.org,1001,,,20,MonetaryBalance,,10,,*monetary,,14,*none -cgrates.org,1001,,,,VoiceBalance,,10,,*voice,,3600000000000, +#Tenant,ID,FilterIDs,ActivationInterval,Weight,BalanceID,BalanceFilterIDs,BalanceWeight,BalanceBlocker,BalanceType,BalanceOpts,BalanceCostIncrements,BalanceCostAttributes,BalanceUnitFactors,BalanceValue,ThresholdIDs +cgrates.org,1001,,,20,MonetaryBalance,,10,,*monetary,,fltr1&fltr2;1.3;2.3;3.3,attr1;attr2,fltr1&fltr2;100;fltr3;200,14,*none +cgrates.org,1001,,,,VoiceBalance,,10,,*voice,,,,,3600000000000, ` ) diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 8f5942685..1297dc95e 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -1638,14 +1638,36 @@ func TestLoadAccountProfiles(t *testing.T) { FilterIDs: []string{}, Weight: 10, Type: utils.MONETARY, - Value: 14, + CostIncrement: []*utils.TPBalanceCostIncrement{ + &utils.TPBalanceCostIncrement{ + FilterIDs: []string{"fltr1", "fltr2"}, + Increment: utils.Float64Pointer(1.3), + FixedFee: utils.Float64Pointer(2.3), + RecurrentFee: utils.Float64Pointer(3.3), + }, + }, + CostAttributes: []string{"attr1", "attr2"}, + UnitFactors: []*utils.TPBalanceUnitFactor{ + &utils.TPBalanceUnitFactor{ + FilterIDs: []string{"fltr1", "fltr2"}, + Factor: 100, + }, + &utils.TPBalanceUnitFactor{ + FilterIDs: []string{"fltr3"}, + Factor: 200, + }, + }, + Value: 14, }, &utils.TPAccountBalance{ - ID: "VoiceBalance", - FilterIDs: []string{}, - Weight: 10, - Type: utils.VOICE, - Value: 3600000000000, + ID: "VoiceBalance", + FilterIDs: []string{}, + Weight: 10, + Type: utils.VOICE, + CostIncrement: []*utils.TPBalanceCostIncrement{}, + CostAttributes: []string{}, + UnitFactors: []*utils.TPBalanceUnitFactor{}, + Value: 3600000000000, }, }, ThresholdIDs: []string{utils.META_NONE}, diff --git a/engine/model_helpers.go b/engine/model_helpers.go index 689beb9de..8a3a9d044 100644 --- a/engine/model_helpers.go +++ b/engine/model_helpers.go @@ -27,6 +27,8 @@ import ( "strings" "time" + "github.com/ericlagergren/decimal" + "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/utils" ) @@ -3511,7 +3513,7 @@ func (apm AccountProfileMdls) CSVHeader() (result []string) { } } -func (tps AccountProfileMdls) AsTPAccountProfile() (result []*utils.TPAccountProfile) { +func (tps AccountProfileMdls) AsTPAccountProfile() (result []*utils.TPAccountProfile, err error) { filterIDsMap := make(map[string]utils.StringMap) thresholdIDsMap := make(map[string]utils.StringMap) actPrfMap := make(map[string]*utils.TPAccountProfile) @@ -3565,14 +3567,53 @@ func (tps AccountProfileMdls) AsTPAccountProfile() (result []*utils.TPAccountPro filterIDs = append(filterIDs, filterAttr) } } + costIncrements := make([]*utils.TPBalanceCostIncrement, 0) + if tp.BalanceCostIncrements != utils.EmptyString { + sls := strings.Split(tp.BalanceCostIncrements, utils.INFIELD_SEP) + if len(sls)%4 != 0 { + return nil, fmt.Errorf("invlid key: <%s> for BalanceCostIncrements", tp.BalanceCostIncrements) + } + for j := 0; j < len(sls); j = j + 4 { + costIncrement, err := utils.NewTPBalanceCostIncrement(sls[j], sls[j+1], sls[j+2], sls[j+3]) + if err != nil { + return nil, err + } + costIncrements = append(costIncrements, costIncrement) + } + } + costAttributes := make([]string, 0) + if tp.BalanceCostAttributes != utils.EmptyString { + costAttributeSplit := strings.Split(tp.BalanceCostAttributes, utils.INFIELD_SEP) + for _, costAttribute := range costAttributeSplit { + costAttributes = append(costAttributes, costAttribute) + } + } + unitFactors := make([]*utils.TPBalanceUnitFactor, 0) + if tp.BalanceUnitFactors != utils.EmptyString { + sls := strings.Split(tp.BalanceUnitFactors, utils.INFIELD_SEP) + if len(sls)%2 != 0 { + return nil, fmt.Errorf("invlid key: <%s> for BalanceUnitFactors", tp.BalanceUnitFactors) + } + + for j := 0; j < len(sls); j = j + 2 { + unitFactor, err := utils.NewTPBalanceUnitFactor(sls[j], sls[j+1]) + if err != nil { + return nil, err + } + unitFactors = append(unitFactors, unitFactor) + } + } aPrf.Balances = append(aPrf.Balances, &utils.TPAccountBalance{ - ID: tp.BalanceID, - FilterIDs: filterIDs, - Weight: tp.BalanceWeight, - Blocker: tp.BalanceBlocker, - Type: tp.BalanceType, - Opts: tp.BalanceOpts, - Value: tp.BalanceValue, + ID: tp.BalanceID, + FilterIDs: filterIDs, + Weight: tp.BalanceWeight, + Blocker: tp.BalanceBlocker, + Type: tp.BalanceType, + Opts: tp.BalanceOpts, + CostIncrement: costIncrements, + CostAttributes: costAttributes, + UnitFactors: unitFactors, + Value: tp.BalanceValue, }) } actPrfMap[tenID] = aPrf @@ -3637,6 +3678,24 @@ func APItoModelTPAccountProfile(tPrf *utils.TPAccountProfile) (mdls AccountProfi mdl.BalanceWeight = balance.Weight mdl.BalanceType = balance.Type mdl.BalanceOpts = balance.Opts + for i, costIncr := range balance.CostIncrement { + if i != 0 { + mdl.BalanceCostIncrements += utils.INFIELD_SEP + } + mdl.BalanceCostIncrements += costIncr.AsString() + } + for i, costAttr := range balance.CostAttributes { + if i != 0 { + mdl.BalanceCostAttributes += utils.INFIELD_SEP + } + mdl.BalanceCostAttributes += costAttr + } + for i, unitFactor := range balance.UnitFactors { + if i != 0 { + mdl.BalanceUnitFactors += utils.INFIELD_SEP + } + mdl.BalanceUnitFactors += unitFactor.AsString() + } mdl.BalanceValue = balance.Value mdls = append(mdls, mdl) i++ @@ -3646,11 +3705,10 @@ func APItoModelTPAccountProfile(tPrf *utils.TPAccountProfile) (mdls AccountProfi func APItoAccountProfile(tpAp *utils.TPAccountProfile, timezone string) (ap *utils.AccountProfile, err error) { ap = &utils.AccountProfile{ - Tenant: tpAp.Tenant, - ID: tpAp.ID, - FilterIDs: make([]string, len(tpAp.FilterIDs)), - Weight: tpAp.Weight, - + Tenant: tpAp.Tenant, + ID: tpAp.ID, + FilterIDs: make([]string, len(tpAp.FilterIDs)), + Weight: tpAp.Weight, Balances: make([]*utils.Balance, len(tpAp.Balances)), ThresholdIDs: make([]string, len(tpAp.ThresholdIDs)), } @@ -3683,6 +3741,32 @@ func APItoAccountProfile(tpAp *utils.TPAccountProfile, timezone string) (ap *uti ap.Balances[i].Opts[keyValSls[0]] = keyValSls[1] } } + if bal.CostIncrement != nil { + ap.Balances[i].CostIncrements = make([]*utils.CostIncrement, len(bal.CostIncrement)) + for j, costIncrement := range bal.CostIncrement { + ap.Balances[i].CostIncrements[j] = &utils.CostIncrement{ + FilterIDs: costIncrement.FilterIDs, + Increment: new(decimal.Big).SetFloat64(*costIncrement.Increment), + FixedFee: new(decimal.Big).SetFloat64(*costIncrement.FixedFee), + RecurrentFee: new(decimal.Big).SetFloat64(*costIncrement.RecurrentFee), + } + } + } + if bal.CostAttributes != nil { + ap.Balances[i].CostAttributes = make([]string, len(bal.CostAttributes)) + for j, costAttribute := range bal.CostAttributes { + ap.Balances[i].CostAttributes[j] = costAttribute + } + } + if bal.UnitFactors != nil { + ap.Balances[i].UnitFactors = make([]*utils.UnitFactor, len(bal.UnitFactors)) + for j, unitFactor := range bal.UnitFactors { + ap.Balances[i].UnitFactors[j] = &utils.UnitFactor{ + FilterIDs: unitFactor.FilterIDs, + Factor: new(decimal.Big).SetFloat64(unitFactor.Factor), + } + } + } } for i, stp := range tpAp.ThresholdIDs { ap.ThresholdIDs[i] = stp diff --git a/engine/model_helpers_test.go b/engine/model_helpers_test.go index ca89c1385..e7b58e3a2 100644 --- a/engine/model_helpers_test.go +++ b/engine/model_helpers_test.go @@ -7012,18 +7012,23 @@ func TestAccountProfileMdlsAsTPAccountProfile(t *testing.T) { Weight: 10.0, Balances: []*utils.TPAccountBalance{ { - ID: "VoiceBalance", - FilterIDs: []string{"FLTR_RES_GR2"}, - Weight: 10, - Type: utils.VOICE, - Value: 3600000000000, + ID: "VoiceBalance", + FilterIDs: []string{"FLTR_RES_GR2"}, + Weight: 10, + Type: utils.VOICE, + CostIncrement: []*utils.TPBalanceCostIncrement{}, + CostAttributes: []string{}, + UnitFactors: []*utils.TPBalanceUnitFactor{}, + Value: 3600000000000, }, }, ThresholdIDs: []string{"WARN_RES1"}, }, } - result := testStruct.AsTPAccountProfile() - if !reflect.DeepEqual(exp, result) { + result, err := testStruct.AsTPAccountProfile() + if err != nil { + t.Error(err) + } else if !reflect.DeepEqual(exp, result) { t.Errorf("Expecting: %+v,\nreceived: %+v", utils.ToJSON(exp), utils.ToJSON(result)) } } @@ -7058,18 +7063,23 @@ func TestAccountProfileMdlsAsTPAccountProfileCase2(t *testing.T) { Weight: 10.0, Balances: []*utils.TPAccountBalance{ { - ID: "VoiceBalance", - FilterIDs: []string{"FLTR_RES_GR2"}, - Weight: 10, - Type: utils.VOICE, - Value: 3600000000000, + ID: "VoiceBalance", + FilterIDs: []string{"FLTR_RES_GR2"}, + Weight: 10, + Type: utils.VOICE, + CostIncrement: []*utils.TPBalanceCostIncrement{}, + CostAttributes: []string{}, + UnitFactors: []*utils.TPBalanceUnitFactor{}, + Value: 3600000000000, }, }, ThresholdIDs: []string{"WARN_RES1"}, }, } - result := testStruct.AsTPAccountProfile() - if !reflect.DeepEqual(exp, result) { + result, err := testStruct.AsTPAccountProfile() + if err != nil { + t.Error(err) + } else if !reflect.DeepEqual(exp, result) { t.Errorf("Expecting: %+v,\nreceived: %+v", utils.ToJSON(exp), utils.ToJSON(result)) } } diff --git a/engine/models.go b/engine/models.go index dcce95951..66683888e 100644 --- a/engine/models.go +++ b/engine/models.go @@ -570,23 +570,25 @@ func (ActionProfileMdl) TableName() string { } type AccountProfileMdl struct { - PK uint `gorm:"primary_key"` - Tpid string - Tenant string `index:"0" re:""` - ID string `index:"1" re:""` - FilterIDs string `index:"2" re:""` - ActivationInterval string `index:"3" re:""` - Weight float64 `index:"4" re:"\d+\.?\d*"` - BalanceID string `index:"5" re:""` - BalanceFilterIDs string `index:"6" re:""` - BalanceWeight float64 `index:"7" re:"\d+\.?\d*"` - BalanceBlocker bool `index:"8" re:""` - BalanceType string `index:"9" re:""` - BalanceOpts string `index:"10" re:""` - BalanceValue float64 `index:"11" re:"\d+\.?\d*"` - ThresholdIDs string `index:"12" re:""` - - CreatedAt time.Time + PK uint `gorm:"primary_key"` + Tpid string + Tenant string `index:"0" re:""` + ID string `index:"1" re:""` + FilterIDs string `index:"2" re:""` + ActivationInterval string `index:"3" re:""` + Weight float64 `index:"4" re:"\d+\.?\d*"` + BalanceID string `index:"5" re:""` + BalanceFilterIDs string `index:"6" re:""` + BalanceWeight float64 `index:"7" re:"\d+\.?\d*"` + BalanceBlocker bool `index:"8" re:""` + BalanceType string `index:"9" re:""` + BalanceOpts string `index:"10" re:""` + BalanceCostIncrements string `index:"11" re:""` + BalanceCostAttributes string `index:"12" re:""` + BalanceUnitFactors string `index:"13" re:""` + BalanceValue float64 `index:"14" re:"\d+\.?\d*"` + ThresholdIDs string `index:"15" re:""` + CreatedAt time.Time } func (AccountProfileMdl) TableName() string { diff --git a/engine/storage_csv.go b/engine/storage_csv.go index 0bb06e650..4fbe883f6 100644 --- a/engine/storage_csv.go +++ b/engine/storage_csv.go @@ -707,7 +707,7 @@ func (csvs *CSVStorage) GetTPAccountProfiles(tpid, tenant, id string) ([]*utils. }); err != nil { return nil, err } - return tpDPPs.AsTPAccountProfile(), nil + return tpDPPs.AsTPAccountProfile() } func (csvs *CSVStorage) GetTpIds(colName string) ([]string, error) { diff --git a/engine/storage_sql.go b/engine/storage_sql.go index 4ab8f1b19..6b41be871 100644 --- a/engine/storage_sql.go +++ b/engine/storage_sql.go @@ -238,7 +238,8 @@ func (self *SQLStorage) RemTpData(table, tpid string, args map[string]string) er utils.TBLTPSharedGroups, utils.TBLTPActions, utils.TBLTPActionTriggers, utils.TBLTPAccountActions, utils.TBLTPResources, utils.TBLTPStats, utils.TBLTPThresholds, utils.TBLTPFilters, utils.TBLTPActionPlans, utils.TBLTPRoutes, utils.TBLTPAttributes, - utils.TBLTPChargers, utils.TBLTPDispatchers, utils.TBLTPDispatcherHosts} { + utils.TBLTPChargers, utils.TBLTPDispatchers, utils.TBLTPDispatcherHosts, utils.TBLTPAccountProfiles, + utils.TBLTPActionProfiles, utils.TBLTPRateProfiles} { if err := tx.Table(tblName).Where("tpid = ?", tpid).Delete(nil).Error; err != nil { tx.Rollback() return err @@ -1664,8 +1665,10 @@ func (self *SQLStorage) GetTPAccountProfiles(tpid, tenant, id string) ([]*utils. if err := q.Find(&dpps).Error; err != nil { return nil, err } - arls := dpps.AsTPAccountProfile() - if len(arls) == 0 { + arls, err := dpps.AsTPAccountProfile() + if err != nil { + return nil, err + } else if len(arls) == 0 { return arls, utils.ErrNotFound } return arls, nil diff --git a/engine/z_stordb_it_test.go b/engine/z_stordb_it_test.go index f6f16895d..2851f62f7 100644 --- a/engine/z_stordb_it_test.go +++ b/engine/z_stordb_it_test.go @@ -42,6 +42,7 @@ var sTestsStorDBit = []func(t *testing.T){ testStorDBitFlush, testStorDBitIsDBEmpty, testStorDBitCRUDVersions, + testStorDBitCRUDTPAccountProfiles, testStorDBitCRUDTPActionProfiles, testStorDBitCRUDTPDispatcherProfiles, testStorDBitCRUDTPDispatcherHosts, @@ -50,7 +51,7 @@ var sTestsStorDBit = []func(t *testing.T){ testStorDBitCRUDTPRoutes, testStorDBitCRUDTPThresholds, testStorDBitCRUDTPAttributes, - testStorDVitCRUDTPChargers, + testStorDBitCRUDTPChargers, testStorDBitCRUDTpTimings, testStorDBitCRUDTpDestinations, testStorDBitCRUDTpRates, @@ -117,7 +118,7 @@ func TestStorDBit(t *testing.T) { split := strings.Split(stestFullName, ".") stestName := split[len(split)-1] // Fixme: Implement mongo needed versions methods - if (*dbType == utils.MetaMongo || *dbType == utils.MetaInternal) && stestName != "testStorDBitCRUDVersions" { + if stestName != "testStorDBitCRUDVersions" { stestName := split[len(split)-1] t.Run(stestName, stest) } @@ -144,6 +145,80 @@ func testStorDBitIsDBEmpty(t *testing.T) { } } +func testStorDBitCRUDTPAccountProfiles(t *testing.T) { + //READ + if _, err := storDB.GetTPAccountProfiles("sub_ID1", utils.EmptyString, "TEST_ID1"); err != utils.ErrNotFound { + t.Error(err) + } + + //WRITE + var actPrf = []*utils.TPAccountProfile{ + &utils.TPAccountProfile{ + TPid: testTPID, + Tenant: "cgrates.org", + ID: "1001", + Weight: 20, + Balances: []*utils.TPAccountBalance{ + &utils.TPAccountBalance{ + ID: "MonetaryBalance", + FilterIDs: []string{}, + Weight: 10, + Type: utils.MONETARY, + CostIncrement: []*utils.TPBalanceCostIncrement{ + &utils.TPBalanceCostIncrement{ + FilterIDs: []string{"fltr1", "fltr2"}, + Increment: utils.Float64Pointer(1.3), + FixedFee: utils.Float64Pointer(2.3), + RecurrentFee: utils.Float64Pointer(3.3), + }, + }, + CostAttributes: []string{"attr1", "attr2"}, + UnitFactors: []*utils.TPBalanceUnitFactor{ + &utils.TPBalanceUnitFactor{ + FilterIDs: []string{"fltr1", "fltr2"}, + Factor: 100, + }, + &utils.TPBalanceUnitFactor{ + FilterIDs: []string{"fltr3"}, + Factor: 200, + }, + }, + Value: 14, + }, + }, + ThresholdIDs: []string{utils.META_NONE}, + }, + } + if err := storDB.SetTPAccountProfiles(actPrf); err != nil { + t.Error(err) + } + + //READ + if rcv, err := storDB.GetTPAccountProfiles(actPrf[0].TPid, utils.EmptyString, utils.EmptyString); err != nil { + t.Error(err) + } else if !(reflect.DeepEqual(rcv[0], actPrf[0])) { + t.Errorf("Expecting:\n%+v\nReceived:\n%+v\n", utils.ToJSON(actPrf[0]), utils.ToJSON(rcv[0])) + } + + //UPDATE AND READ + actPrf[0].FilterIDs = []string{"*string:~*req.Account:1007"} + if err := storDB.SetTPAccountProfiles(actPrf); err != nil { + t.Error(err) + } else if rcv, err := storDB.GetTPAccountProfiles(actPrf[0].TPid, + utils.EmptyString, utils.EmptyString); err != nil { + t.Error(err) + } else if !(reflect.DeepEqual(rcv[0], actPrf[0])) { + t.Errorf("Expecting:\n%+v\nReceived:\n%+v\n", utils.ToJSON(actPrf[0]), utils.ToJSON(rcv[0])) + } + + //REMOVE AND READ + if err := storDB.RemTpData(utils.EmptyString, actPrf[0].TPid, nil); err != nil { + t.Error(err) + } else if _, err := storDB.GetTPActionProfiles(actPrf[0].TPid, utils.EmptyString, utils.EmptyString); err != utils.ErrNotFound { + t.Error(err) + } +} + func testStorDBitCRUDTPActionProfiles(t *testing.T) { //READ if _, err := storDB.GetTPActionProfiles("sub_ID1", utils.EmptyString, "TEST_ID1"); err != utils.ErrNotFound { @@ -618,7 +693,7 @@ func testStorDBitCRUDTPThresholds(t *testing.T) { MinSleep: "1s", Blocker: true, Weight: 10, - ActionIDs: []string{"Thresh1", "Thresh2"}, + ActionIDs: []string{"Thresh1"}, Async: true, }, { @@ -662,10 +737,10 @@ func testStorDBitCRUDTPThresholds(t *testing.T) { //READ if rcv, err := storDB.GetTPThresholds(tpThresholds[0].TPid, utils.EmptyString, utils.EmptyString); err != nil { t.Error(err) - } else if !(reflect.DeepEqual(tpThresholds[0], rcv[0]) || - reflect.DeepEqual(tpThresholds[0], rcv[1])) { + } else if !reflect.DeepEqual(tpThresholds[0], rcv[0]) && + !reflect.DeepEqual(tpThresholds[0], rcv[1]) { t.Errorf("Expecting:\n%+v\nReceived:\n%+v\n||\n%+v", - utils.ToIJSON(tpThresholds[0]), utils.ToIJSON(rcv[0]), utils.ToIJSON(rcv[1])) + utils.ToIJSON(tpThresholds[0]), utils.ToJSON(rcv[0]), utils.ToJSON(rcv[1])) } @@ -692,13 +767,13 @@ func testStorDBitCRUDTPAttributes(t *testing.T) { Attributes: []*utils.TPAttribute{ { Type: utils.MetaString, - Path: utils.MetaReq + utils.AccountField + utils.InInFieldSep, + Path: utils.MetaReq + utils.NestingSep + utils.AccountField + utils.InInFieldSep, Value: "102", FilterIDs: []string{"*string:~*req.Account:102"}, }, { Type: utils.MetaString, - Path: utils.MetaReq + utils.AccountField + utils.InInFieldSep, + Path: utils.MetaReq + utils.NestingSep + utils.AccountField + utils.InInFieldSep, Value: "101", FilterIDs: []string{"*string:~*req.Account:101"}, }, @@ -711,13 +786,13 @@ func testStorDBitCRUDTPAttributes(t *testing.T) { Attributes: []*utils.TPAttribute{ { Type: utils.MetaString, - Path: utils.MetaReq + utils.Destination + utils.InInFieldSep, + Path: utils.MetaReq + utils.NestingSep + utils.Destination + utils.InInFieldSep, Value: "11", FilterIDs: []string{"*string:~*req.Destination:11"}, }, { Type: utils.MetaString, - Path: utils.MetaReq + utils.Destination + utils.InInFieldSep, + Path: utils.MetaReq + utils.NestingSep + utils.Destination + utils.InInFieldSep, Value: "11", FilterIDs: []string{"*string:~*req.Destination:10"}, }, @@ -734,7 +809,7 @@ func testStorDBitCRUDTPAttributes(t *testing.T) { } else if !(reflect.DeepEqual(rcv[0], tpAProfile[0]) || reflect.DeepEqual(rcv[1], tpAProfile[0])) { t.Errorf("Expecting:\n%+v\nReceived:\n%+v\n||\n%+v", - utils.ToIJSON(tpAProfile[0]), utils.ToIJSON(rcv[0]), utils.ToIJSON(rcv[1])) + utils.ToIJSON(tpAProfile[0]), utils.ToJSON(rcv[0]), utils.ToJSON(rcv[1])) } //UPDATE @@ -761,7 +836,7 @@ func testStorDBitCRUDTPAttributes(t *testing.T) { } } -func testStorDVitCRUDTPChargers(t *testing.T) { +func testStorDBitCRUDTPChargers(t *testing.T) { //READ if _, err := storDB.GetTPChargers("TP_ID", utils.EmptyString, utils.EmptyString); err != utils.ErrNotFound { t.Error(err) diff --git a/loaders/loader.go b/loaders/loader.go index 91ed716ab..92a75dc2f 100644 --- a/loaders/loader.go +++ b/loaders/loader.go @@ -662,8 +662,11 @@ func (ldr *Loader) storeLoadedData(loaderType string, return } } - - for _, tpAcp := range acpsModels.AsTPAccountProfile() { + accountTPModels, err := acpsModels.AsTPAccountProfile() + if err != nil { + return err + } + for _, tpAcp := range accountTPModels { acp, err := engine.APItoAccountProfile(tpAcp, ldr.timezone) if err != nil { return err diff --git a/utils/accountprofile.go b/utils/accountprofile.go index 37d43079f..c329993ca 100644 --- a/utils/accountprofile.go +++ b/utils/accountprofile.go @@ -46,17 +46,11 @@ type Balance struct { Type string Opts map[string]interface{} CostIncrements []*CostIncrement - CostAttributes []*CostAttributes + CostAttributes []string 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 diff --git a/utils/apitpdata.go b/utils/apitpdata.go index 306ef7a99..6da72a0a3 100755 --- a/utils/apitpdata.go +++ b/utils/apitpdata.go @@ -21,6 +21,7 @@ package utils import ( "fmt" "sort" + "strconv" "strings" "time" ) @@ -1554,13 +1555,93 @@ type TPAccountProfile struct { } type TPAccountBalance struct { - ID string + ID string + FilterIDs []string + Weight float64 + Blocker bool + Type string + Opts string + CostIncrement []*TPBalanceCostIncrement + CostAttributes []string + UnitFactors []*TPBalanceUnitFactor + Value float64 +} + +func NewTPBalanceCostIncrement(filtersStr, incrementStr, fixedFeeStr, recurrentFeeStr string) (costIncrement *TPBalanceCostIncrement, err error) { + costIncrement = &TPBalanceCostIncrement{ + FilterIDs: strings.Split(filtersStr, ANDSep), + } + if incrementStr != EmptyString { + incr, err := strconv.ParseFloat(incrementStr, 64) + if err != nil { + return nil, err + } + costIncrement.Increment = Float64Pointer(incr) + } + if fixedFeeStr != EmptyString { + fixedFee, err := strconv.ParseFloat(fixedFeeStr, 64) + if err != nil { + return nil, err + } + costIncrement.FixedFee = Float64Pointer(fixedFee) + } + if recurrentFeeStr != EmptyString { + recFee, err := strconv.ParseFloat(recurrentFeeStr, 64) + if err != nil { + return nil, err + } + costIncrement.RecurrentFee = Float64Pointer(recFee) + } + return +} + +type TPBalanceCostIncrement struct { + FilterIDs []string + Increment *float64 + FixedFee *float64 + RecurrentFee *float64 +} + +func (costIncr *TPBalanceCostIncrement) AsString() (s string) { + if len(costIncr.FilterIDs) != 0 { + s = s + strings.Join(costIncr.FilterIDs, ANDSep) + } + s = s + INFIELD_SEP + if costIncr.Increment != nil { + s = s + strconv.FormatFloat(*costIncr.Increment, 'f', -1, 64) + } + s = s + INFIELD_SEP + if costIncr.FixedFee != nil { + s = s + strconv.FormatFloat(*costIncr.FixedFee, 'f', -1, 64) + } + s = s + INFIELD_SEP + if costIncr.RecurrentFee != nil { + s = s + strconv.FormatFloat(*costIncr.RecurrentFee, 'f', -1, 64) + } + return +} + +func NewTPBalanceUnitFactor(filtersStr, factorStr string) (unitFactor *TPBalanceUnitFactor, err error) { + unitFactor = &TPBalanceUnitFactor{ + FilterIDs: strings.Split(filtersStr, ANDSep), + } + if unitFactor.Factor, err = strconv.ParseFloat(factorStr, 64); err != nil { + return + } + return +} + +type TPBalanceUnitFactor struct { FilterIDs []string - Weight float64 - Blocker bool - Type string - Opts string - Value float64 + Factor float64 +} + +func (unitFactor *TPBalanceUnitFactor) AsString() (s string) { + if len(unitFactor.FilterIDs) != 0 { + s = s + strings.Join(unitFactor.FilterIDs, ANDSep) + } + s = s + INFIELD_SEP + strconv.FormatFloat(unitFactor.Factor, 'f', -1, 64) + return } // ArgActionSv1ScheduleActions is used in ActionSv1 methods