diff --git a/apier/v1/apier.go b/apier/v1/apier.go index 12aa191ee..de4af2ead 100644 --- a/apier/v1/apier.go +++ b/apier/v1/apier.go @@ -116,6 +116,7 @@ func (self *ApierV1) GetAccount(attr *utils.AttrGetAccount, reply *engine.Accoun type AttrAddBalance struct { Tenant string Account string + BalanceUuid string BalanceId string BalanceType string Direction string @@ -126,6 +127,7 @@ type AttrAddBalance struct { Weight float64 SharedGroup string Overwrite bool // When true it will reset if the balance is already there + Disabled bool } func (self *ApierV1) AddBalance(attr *AttrAddBalance, reply *string) error { @@ -164,6 +166,7 @@ func (self *ApierV1) AddBalance(attr *AttrAddBalance, reply *string) error { BalanceType: attr.BalanceType, Direction: attr.Direction, Balance: &engine.Balance{ + Uuid: attr.BalanceUuid, Id: attr.BalanceId, Value: attr.Value, ExpirationDate: expTime, @@ -171,6 +174,48 @@ func (self *ApierV1) AddBalance(attr *AttrAddBalance, reply *string) error { DestinationIds: attr.DestinationId, Weight: attr.Weight, SharedGroup: attr.SharedGroup, + Disabled: attr.Disabled, + }, + }, + }) + if err := at.Execute(); err != nil { + *reply = err.Error() + return err + } + *reply = OK + return nil +} + +func (self *ApierV1) EnableDisableBalance(attr *AttrAddBalance, reply *string) error { + expTime, err := utils.ParseDate(attr.ExpiryTime) + if err != nil { + *reply = err.Error() + return err + } + tag := utils.ConcatenatedKey(attr.Direction, attr.Tenant, attr.Account) + if _, err := self.AccountDb.GetAccount(tag); err != nil { + return utils.ErrNotFound + } + at := &engine.ActionPlan{ + AccountIds: []string{tag}, + } + if attr.Direction == "" { + attr.Direction = engine.OUTBOUND + } + at.SetActions(engine.Actions{ + &engine.Action{ + ActionType: engine.ENABLE_DISABLE_BALANCE, + BalanceType: attr.BalanceType, + Direction: attr.Direction, + Balance: &engine.Balance{ + Id: attr.BalanceId, + Value: attr.Value, + ExpirationDate: expTime, + RatingSubject: attr.RatingSubject, + DestinationIds: attr.DestinationId, + Weight: attr.Weight, + SharedGroup: attr.SharedGroup, + Disabled: attr.Disabled, }, }, }) diff --git a/console/balance_enabledisable.go b/console/balance_enabledisable.go new file mode 100644 index 000000000..975d19ae7 --- /dev/null +++ b/console/balance_enabledisable.go @@ -0,0 +1,65 @@ +/* +Rating system designed to be used in VoIP Carriers World +Copyright (C) 2012-2015 ITsysCOM + +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/apier/v1" + "github.com/cgrates/cgrates/utils" +) + +func init() { + c := &CmdEnableDisableBalance{ + name: "balance_enabledisable", + rpcMethod: "ApierV1.EnableDisableBalance", + } + commands[c.Name()] = c + c.CommandExecuter = &CommandExecuter{c} +} + +// Commander implementation +type CmdEnableDisableBalance struct { + name string + rpcMethod string + rpcParams *v1.AttrAddBalance + *CommandExecuter +} + +func (self *CmdEnableDisableBalance) Name() string { + return self.name +} + +func (self *CmdEnableDisableBalance) RpcMethod() string { + return self.rpcMethod +} + +func (self *CmdEnableDisableBalance) RpcParams(reset bool) interface{} { + if reset || self.rpcParams == nil { + self.rpcParams = &v1.AttrAddBalance{BalanceType: utils.MONETARY, Overwrite: false} + } + return self.rpcParams +} + +func (self *CmdEnableDisableBalance) PostprocessRpcParams() error { + return nil +} + +func (self *CmdEnableDisableBalance) RpcResult() interface{} { + var s string + return &s +} diff --git a/data/storage/mysql/create_tariffplan_tables.sql b/data/storage/mysql/create_tariffplan_tables.sql index 7dae1bd0b..43de3ae8e 100644 --- a/data/storage/mysql/create_tariffplan_tables.sql +++ b/data/storage/mysql/create_tariffplan_tables.sql @@ -162,6 +162,7 @@ CREATE TABLE `tp_actions` ( `balance_weight` DECIMAL(8,2) NOT NULL, `extra_parameters` varchar(256) NOT NULL, `weight` DECIMAL(8,2) NOT NULL, + `disabled` BOOLEAN NOT NULL, `created_at` TIMESTAMP, PRIMARY KEY (`id`), KEY `tpid` (`tpid`), diff --git a/data/storage/postgres/create_tariffplan_tables.sql b/data/storage/postgres/create_tariffplan_tables.sql index 506b469b3..e5eeaff7d 100644 --- a/data/storage/postgres/create_tariffplan_tables.sql +++ b/data/storage/postgres/create_tariffplan_tables.sql @@ -157,6 +157,7 @@ CREATE TABLE tp_actions ( balance_weight NUMERIC(8,2) NOT NULL, extra_parameters VARCHAR(256) NOT NULL, weight NUMERIC(8,2) NOT NULL, + disabled BOOLEAN NOT NULL, created_at TIMESTAMP, UNIQUE (tpid, tag, action, balance_tag, balance_type, direction, expiry_time, timing_tags, destination_tags, shared_group, balance_weight, weight) ); diff --git a/data/tariffplans/cdrstats/Actions.csv b/data/tariffplans/cdrstats/Actions.csv index 899e4928e..216f22141 100644 --- a/data/tariffplans/cdrstats/Actions.csv +++ b/data/tariffplans/cdrstats/Actions.csv @@ -1,2 +1,2 @@ #ActionsTag,Action,BalanceTag,BalanceType,Direction,Units,ExpiryTime,TimingTags,DestinationTag,RatingSubject,Category,BalanceWeight,SharedGroup,ExtraParameters,Weight -CDRST_LOG,*log,,,,,,,,,,,,,10 +CDRST_LOG,*log,,,,,,,,,,,,,false,10 diff --git a/data/tariffplans/prepaid1centpsec/Actions.csv b/data/tariffplans/prepaid1centpsec/Actions.csv index 181f39c86..6b50b35ef 100644 --- a/data/tariffplans/prepaid1centpsec/Actions.csv +++ b/data/tariffplans/prepaid1centpsec/Actions.csv @@ -1,6 +1,6 @@ -#ActionsTag[0],Action[1],ActionExtraParameters[2],BalanceTag[3],BalanceType[4],Direction[5],Category[6],DestinationTag[7],RatingSubject[8],SharedGroup[9],ExpiryTime[10],TimingTags[11],Units[12],BalanceWeight[13],Weight[14] -PREPAID_10,*topup_reset,,,*monetary,*out,,*any,,,*unlimited,,10,10,10 -BONUS_1,*topup,,,*monetary,*out,,*any,,,*unlimited,,1,10,10 -LOG_BALANCE,*log,,,,,,,,,,,,,10 -CDRST_WARN_HTTP,*call_url,http://localhost:8080,,,,,,,,,,,,10 -CDRST_LOG,*log,,,,,,,,,,,,,10 +#ActionsTag[0],Action[1],ActionExtraParameters[2],BalanceTag[3],BalanceType[4],Direction[5],Category[6],DestinationTag[7],RatingSubject[8],SharedGroup[9],ExpiryTime[10],TimingTags[11],Units[12],BalanceWeight[13],BalanceDisabled[14],Weight[15] +PREPAID_10,*topup_reset,,,*monetary,*out,,*any,,,*unlimited,,10,10,false,10 +BONUS_1,*topup,,,*monetary,*out,,*any,,,*unlimited,,1,10,false,10 +LOG_BALANCE,*log,,,,,,,,,,,,,false,10 +CDRST_WARN_HTTP,*call_url,http://localhost:8080,,,,,,,,,,,,false,10 +CDRST_LOG,*log,,,,,,,,,,,,,false,10 diff --git a/data/tariffplans/tutorial/Actions.csv b/data/tariffplans/tutorial/Actions.csv index 4c08a73c3..cb3537311 100644 --- a/data/tariffplans/tutorial/Actions.csv +++ b/data/tariffplans/tutorial/Actions.csv @@ -1,10 +1,10 @@ -#ActionsTag[0],Action[1],ExtraParameters[2],BalanceTag[3],BalanceType[4],Direction[5],Category[6],DestinationTag[7],RatingSubject[8],SharedGroup[9],ExpiryTime[10],TimingTags[11],Units[12],BalanceWeight[13],Weight[14] -TOPUP_RST_10,*topup_reset,,,*monetary,*out,,*any,,,*unlimited,,10,10,10 -TOPUP_RST_5,*topup_reset,,,*monetary,*out,,*any,,,*unlimited,,5,20,10 -TOPUP_RST_5,*topup_reset,,,*voice,*out,,DST_1002,SPECIAL_1002,,*unlimited,,90,20,10 -TOPUP_120_DST1003,*topup_reset,,,*voice,*out,,DST_1003,,,*unlimited,,120,20,10 -TOPUP_RST_SHARED_5,*topup,,,*monetary,*out,,*any,,SHARED_A,*unlimited,,5,10,10 -SHARED_A_0,*topup_reset,,,*monetary,*out,,*any,,SHARED_A,*unlimited,,0,10,10 -LOG_WARNING,*log,,,,,,,,,,,,,10 -DISABLE_AND_LOG,*log,,,,,,,,,,,,,10 -DISABLE_AND_LOG,*disable_account,,,,,,,,,,,,,10 +#ActionsTag[0],Action[1],ExtraParameters[2],BalanceTag[3],BalanceType[4],Direction[5],Category[6],DestinationTag[7],RatingSubject[8],SharedGroup[9],ExpiryTime[10],TimingTags[11],Units[12],BalanceWeight[13],BalanceDisabled[14],Weight[15] +TOPUP_RST_10,*topup_reset,,,*monetary,*out,,*any,,,*unlimited,,10,10,,false,10 +TOPUP_RST_5,*topup_reset,,,*monetary,*out,,*any,,,*unlimited,,5,20,,false,10 +TOPUP_RST_5,*topup_reset,,,*voice,*out,,DST_1002,SPECIAL_1002,,*unlimited,,90,20,,false,10 +TOPUP_120_DST1003,*topup_reset,,,*voice,*out,,DST_1003,,,*unlimited,,120,20,,false,10 +TOPUP_RST_SHARED_5,*topup,,,*monetary,*out,,*any,,SHARED_A,*unlimited,,5,10,,false,10 +SHARED_A_0,*topup_reset,,,*monetary,*out,,*any,,SHARED_A,*unlimited,,0,10,,false,10 +LOG_WARNING,*log,,,,,,,,,,,,,,false,10 +DISABLE_AND_LOG,*log,,,,,,,,,,,,,,false,10 +DISABLE_AND_LOG,*disable_account,,,,,,,,,,,,,,false,10 diff --git a/engine/account.go b/engine/account.go index d4ced0431..b086b539f 100644 --- a/engine/account.go +++ b/engine/account.go @@ -96,10 +96,7 @@ func (ub *Account) getCreditForPrefix(cd *CallDescriptor) (duration time.Duratio // Returns the remaining credit in user's balance. func (ub *Account) debitBalanceAction(a *Action, reset bool) error { if a == nil { - return errors.New("nil minute action") - } - if a.Balance.Uuid == "" { - a.Balance.Uuid = utils.GenUUID() + return errors.New("nil action") } bClone := a.Balance.Clone() @@ -127,6 +124,9 @@ func (ub *Account) debitBalanceAction(a *Action, reset bool) error { bClone.SetValue(-bClone.GetValue()) } bClone.dirty = true // Mark the balance as dirty since we have modified and it should be checked by action triggers + if bClone.Uuid == "" { + bClone.Uuid = utils.GenUUID() + } ub.BalanceMap[id] = append(ub.BalanceMap[id], bClone) } if a.Balance.SharedGroup != "" { @@ -147,9 +147,36 @@ func (ub *Account) debitBalanceAction(a *Action, reset bool) error { return nil //ub.BalanceMap[id].GetTotalValue() } +func (ub *Account) enableDisableBalanceAction(a *Action) error { + if a == nil { + return errors.New("nil action") + } + + if ub.BalanceMap == nil { + ub.BalanceMap = make(map[string]BalanceChain) + } + found := false + id := a.BalanceType + a.Direction + ub.CleanExpiredBalances() + for _, b := range ub.BalanceMap[id] { + if b.MatchFilter(a.Balance) { + b.Disabled = a.Balance.Disabled + b.dirty = true + found = true + } + } + if !found { + return utils.ErrNotFound + } + return nil +} + func (ub *Account) getBalancesForPrefix(prefix, category string, balances BalanceChain, sharedGroup string) BalanceChain { var usefulBalances BalanceChain for _, b := range balances { + if b.Disabled { + continue + } if b.IsExpired() || (b.SharedGroup == "" && b.GetValue() <= 0) { continue } diff --git a/engine/account_test.go b/engine/account_test.go index 965e9352e..6a66d6916 100644 --- a/engine/account_test.go +++ b/engine/account_test.go @@ -812,11 +812,25 @@ func TestAccountdebitBalance(t *testing.T) { newMb := &Balance{Weight: 20, DestinationIds: "NEW"} a := &Action{BalanceType: utils.VOICE, Direction: OUTBOUND, Balance: newMb} ub.debitBalanceAction(a, false) - if len(ub.BalanceMap[utils.VOICE+OUTBOUND]) != 3 || ub.BalanceMap[utils.VOICE+OUTBOUND][2].Uuid != newMb.Uuid { + if len(ub.BalanceMap[utils.VOICE+OUTBOUND]) != 3 || ub.BalanceMap[utils.VOICE+OUTBOUND][2].DestinationIds != newMb.DestinationIds { t.Errorf("Error adding minute bucket! %d %+v %+v", len(ub.BalanceMap[utils.VOICE+OUTBOUND]), ub.BalanceMap[utils.VOICE+OUTBOUND][2], newMb) } } +func TestAccountDisableBalance(t *testing.T) { + ub := &Account{ + Id: "rif", + AllowNegative: true, + BalanceMap: map[string]BalanceChain{utils.SMS: BalanceChain{&Balance{Value: 14}}, utils.DATA: BalanceChain{&Balance{Value: 1204}}, utils.VOICE + OUTBOUND: BalanceChain{&Balance{Weight: 20, DestinationIds: "NAT"}, &Balance{Weight: 10, DestinationIds: "RET"}}}, + } + newMb := &Balance{Weight: 20, DestinationIds: "NAT", Disabled: true} + a := &Action{BalanceType: utils.VOICE, Direction: OUTBOUND, Balance: newMb} + ub.enableDisableBalanceAction(a) + if len(ub.BalanceMap[utils.VOICE+OUTBOUND]) != 2 || ub.BalanceMap[utils.VOICE+OUTBOUND][0].Disabled != true { + t.Errorf("Error disabling balance! %d %+v %+v", len(ub.BalanceMap[utils.VOICE+OUTBOUND]), ub.BalanceMap[utils.VOICE+OUTBOUND][0], newMb) + } +} + func TestAccountdebitBalanceExists(t *testing.T) { ub := &Account{ diff --git a/engine/action.go b/engine/action.go index 0f44c9404..e528ab4a8 100644 --- a/engine/action.go +++ b/engine/action.go @@ -48,27 +48,28 @@ type Action struct { } const ( - LOG = "*log" - RESET_TRIGGERS = "*reset_triggers" - SET_RECURRENT = "*set_recurrent" - UNSET_RECURRENT = "*unset_recurrent" - ALLOW_NEGATIVE = "*allow_negative" - DENY_NEGATIVE = "*deny_negative" - RESET_ACCOUNT = "*reset_account" - REMOVE_ACCOUNT = "*remove_account" - TOPUP_RESET = "*topup_reset" - TOPUP = "*topup" - DEBIT_RESET = "*debit_reset" - DEBIT = "*debit" - RESET_COUNTER = "*reset_counter" - RESET_COUNTERS = "*reset_counters" - ENABLE_ACCOUNT = "*enable_account" - DISABLE_ACCOUNT = "*disable_account" - CALL_URL = "*call_url" - CALL_URL_ASYNC = "*call_url_async" - MAIL_ASYNC = "*mail_async" - UNLIMITED = "*unlimited" - CDRLOG = "*cdrlog" + LOG = "*log" + RESET_TRIGGERS = "*reset_triggers" + SET_RECURRENT = "*set_recurrent" + UNSET_RECURRENT = "*unset_recurrent" + ALLOW_NEGATIVE = "*allow_negative" + DENY_NEGATIVE = "*deny_negative" + RESET_ACCOUNT = "*reset_account" + REMOVE_ACCOUNT = "*remove_account" + TOPUP_RESET = "*topup_reset" + TOPUP = "*topup" + DEBIT_RESET = "*debit_reset" + DEBIT = "*debit" + RESET_COUNTER = "*reset_counter" + RESET_COUNTERS = "*reset_counters" + ENABLE_ACCOUNT = "*enable_account" + DISABLE_ACCOUNT = "*disable_account" + ENABLE_DISABLE_BALANCE = "*enable_disable_balance" + CALL_URL = "*call_url" + CALL_URL_ASYNC = "*call_url_async" + MAIL_ASYNC = "*mail_async" + UNLIMITED = "*unlimited" + CDRLOG = "*cdrlog" ) func (a *Action) Clone() *Action { @@ -120,6 +121,8 @@ func getActionFunc(typ string) (actionTypeFunc, bool) { return enableUserAction, true case DISABLE_ACCOUNT: return disableUserAction, true + case ENABLE_DISABLE_BALANCE: + return enableDisableBalanceAction, true case CALL_URL: return callUrl, true case CALL_URL_ASYNC: @@ -407,6 +410,14 @@ func disableUserAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Acti return } +func enableDisableBalanceAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions) (err error) { + if ub == nil { + return errors.New("nil user balance") + } + ub.enableDisableBalanceAction(a) + return +} + func genericReset(ub *Account) error { for k, _ := range ub.BalanceMap { ub.BalanceMap[k] = BalanceChain{&Balance{Value: 0}} diff --git a/engine/balances.go b/engine/balances.go index a75b72f5b..b93fa12db 100644 --- a/engine/balances.go +++ b/engine/balances.go @@ -43,6 +43,7 @@ type Balance struct { SharedGroup string Timings []*RITiming TimingIDs string + Disabled bool precision int account *Account // used to store ub reference for shared balances dirty bool @@ -57,16 +58,21 @@ func (b *Balance) Equal(o *Balance) bool { } bDestIds := b.sortDestinationIds() oDestIds := o.sortDestinationIds() - return b.Id == o.Id && + return b.Uuid == o.Uuid && + b.Id == o.Id && b.ExpirationDate.Equal(o.ExpirationDate) && b.Weight == o.Weight && bDestIds == oDestIds && b.RatingSubject == o.RatingSubject && b.Category == o.Category && - b.SharedGroup == o.SharedGroup + b.SharedGroup == o.SharedGroup && + b.Disabled == o.Disabled } func (b *Balance) MatchFilter(o *Balance) bool { + if o.Uuid != "" { + return b.Uuid == o.Uuid + } if o.Id != "" { return b.Id == o.Id } @@ -93,7 +99,8 @@ func (b *Balance) IsDefault() bool { b.Category == "" && b.ExpirationDate.IsZero() && b.SharedGroup == "" && - b.Weight == 0 + b.Weight == 0 && + b.Disabled == false } func (b *Balance) IsExpired() bool { @@ -105,6 +112,9 @@ func (b *Balance) IsActive() bool { } func (b *Balance) IsActiveAt(t time.Time) bool { + if b.Disabled { + return false + } if len(b.Timings) == 0 { return true } @@ -182,6 +192,7 @@ func (b *Balance) Clone() *Balance { SharedGroup: b.SharedGroup, TimingIDs: b.TimingIDs, Timings: b.Timings, // should not be a problem with aliasing + Disabled: b.Disabled, dirty: b.dirty, } } diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index f78b147fc..a09c2834e 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -153,18 +153,18 @@ SG3,*any,*lowest, *in,cgrates.org,call,*any,*any,*any,LCR_STANDARD,*lowest_cost,,2012-01-01T00:00:00Z,20 ` actions = ` -MINI,*topup_reset,,,*monetary,*out,,,,,*unlimited,,10,10,10 -MINI,*topup,,,*voice,*out,,NAT,test,,*unlimited,,100,10,10 -SHARED,*topup,,,*monetary,*out,,,,SG1,*unlimited,,100,10,10 -TOPUP10_AC,*topup_reset,,,*monetary,*out,,*any,,,*unlimited,,1,10,10 -TOPUP10_AC1,*topup_reset,,,*voice,*out,,DST_UK_Mobile_BIG5,discounted_minutes,,*unlimited,,40,10,10 -SE0,*topup_reset,,,*monetary,*out,,,,SG2,*unlimited,,0,10,10 -SE10,*topup_reset,,,*monetary,*out,,,,SG2,*unlimited,,10,5,10 -SE10,*topup,,,*monetary,*out,,,,,*unlimited,,10,10,10 -EE0,*topup_reset,,,*monetary,*out,,,,SG3,*unlimited,,0,10,10 -EE0,*allow_negative,,,*monetary,*out,,,,,*unlimited,,0,10,10 -DEFEE,*cdrlog,"{""Category"":""^ddi"",""MediationRunId"":""^did_run""}",,,,,,,,,,,,10 -NEG,*allow_negative,,,*monetary,*out,,,,,*unlimited,,0,10,10 +MINI,*topup_reset,,,*monetary,*out,,,,,*unlimited,,10,10,false,10 +MINI,*topup,,,*voice,*out,,NAT,test,,*unlimited,,100,10,false,10 +SHARED,*topup,,,*monetary,*out,,,,SG1,*unlimited,,100,10,false,10 +TOPUP10_AC,*topup_reset,,,*monetary,*out,,*any,,,*unlimited,,1,10,false,10 +TOPUP10_AC1,*topup_reset,,,*voice,*out,,DST_UK_Mobile_BIG5,discounted_minutes,,*unlimited,,40,10,false,10 +SE0,*topup_reset,,,*monetary,*out,,,,SG2,*unlimited,,0,10,false,10 +SE10,*topup_reset,,,*monetary,*out,,,,SG2,*unlimited,,10,5,false,10 +SE10,*topup,,,*monetary,*out,,,,,*unlimited,,10,10,false,10 +EE0,*topup_reset,,,*monetary,*out,,,,SG3,*unlimited,,0,10,false,10 +EE0,*allow_negative,,,*monetary,*out,,,,,*unlimited,,0,10,false,10 +DEFEE,*cdrlog,"{""Category"":""^ddi"",""MediationRunId"":""^did_run""}",,,,,,,,,,,,false,10 +NEG,*allow_negative,,,*monetary,*out,,,,,*unlimited,,0,10,false,10 ` actionTimings = ` MORE_MINUTES,MINI,ONE_TIME_RUN,10 diff --git a/engine/model_helpers_test.go b/engine/model_helpers_test.go index 61f5f0ae6..a9f191d07 100644 --- a/engine/model_helpers_test.go +++ b/engine/model_helpers_test.go @@ -265,8 +265,8 @@ func TestTPActionsAsExportSlice(t *testing.T) { }, } expectedSlc := [][]string{ - []string{"TEST_ACTIONS", "*topup_reset", "", "", "*monetary", utils.OUT, "call", "*any", "special1", "GROUP1", "*never", "", "5", "10", "10"}, - []string{"TEST_ACTIONS", "*http_post", "http://localhost/¶m1=value1", "", "", "", "", "", "", "", "", "", "0", "0", "20"}, + []string{"TEST_ACTIONS", "*topup_reset", "", "", "*monetary", utils.OUT, "call", "*any", "special1", "GROUP1", "*never", "", "5", "10", "false", "10"}, + []string{"TEST_ACTIONS", "*http_post", "http://localhost/¶m1=value1", "", "", "", "", "", "", "", "", "", "0", "0", "false", "20"}, } ms := APItoModelAction(tpActs) diff --git a/engine/models.go b/engine/models.go index 9f2e517b3..e15bde880 100644 --- a/engine/models.go +++ b/engine/models.go @@ -167,7 +167,8 @@ type TpAction struct { TimingTags string `index:"11" re:"[0-9A-Za-z_;]*|\*any"` Units float64 `index:"12" re:"\d+\s*"` BalanceWeight float64 `index:"13" re:"\d+\.?\d*\s*"` - Weight float64 `index:"14" re:"\d+\.?\d*\s*"` + BalanceDisabled bool `index:"14"` + Weight float64 `index:"15" re:"\d+\.?\d*\s*"` CreatedAt time.Time }