From 77d2048cab82194fd8211248e2b5b67eeb11a435 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 17 Jun 2015 20:24:29 +0300 Subject: [PATCH 1/7] better rating info --- engine/balances.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/engine/balances.go b/engine/balances.go index ca4678913..56fac1c74 100644 --- a/engine/balances.go +++ b/engine/balances.go @@ -314,6 +314,12 @@ func (b *Balance) DebitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala }, } prefix, destid := b.getMatchingPrefixAndDestId(cd.Destination) + if prefix == "" { + prefix = cd.Destination + } + if destid == "" { + destid = utils.ANY + } ts.setRatingInfo(&RatingInfo{ MatchedSubject: b.Uuid, MatchedPrefix: prefix, From 81c7593726178ec9077d6abf925670083627ca1f Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 17 Jun 2015 21:56:39 +0300 Subject: [PATCH 2/7] created test for negative debit --- engine/calldesc_test.go | 38 ++++++++++++++++++++++++++++++++++++++ engine/loader_csv_test.go | 15 +++++++++------ 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/engine/calldesc_test.go b/engine/calldesc_test.go index 4d6291b0c..2e3d77fdb 100644 --- a/engine/calldesc_test.go +++ b/engine/calldesc_test.go @@ -906,6 +906,44 @@ func TestDebitFromEmptyShare(t *testing.T) { } } +func TestDebitNegative(t *testing.T) { + ap, _ := ratingStorage.GetActionPlans("POST_AT") + for _, at := range ap { + at.Execute() + } + + cd := &CallDescriptor{ + TimeStart: time.Date(2013, 10, 21, 18, 34, 0, 0, time.UTC), + TimeEnd: time.Date(2013, 10, 21, 18, 34, 5, 0, time.UTC), + Direction: "*out", + Category: "0", + Tenant: "vdf", + Subject: "rif", + Account: "post", + Destination: "0723", + } + + cc, err := cd.MaxDebit() + //utils.PrintFull(cc) + if err != nil || cc.Cost != 2.5 { + t.Errorf("Debit from empty share error: %+v, %v", cc, err) + } + acc, _ := cd.getAccount() + //utils.PrintFull(acc) + balanceMap := acc.BalanceMap[utils.MONETARY+OUTBOUND] + if len(balanceMap) != 1 || balanceMap[0].Value != -2.5 { + t.Errorf("Error debiting from empty share: %+v", balanceMap[0].Value) + } + cc, err = cd.MaxDebit() + //utils.PrintFull(cc) + if err != nil || cc.Cost != 2.5 { + t.Errorf("Debit from empty share error: %+v, %v", cc, err) + } + if len(balanceMap) != 1 || balanceMap[0].Value != -5 { + t.Errorf("Error debiting from empty share: %+v", balanceMap[0].Value) + } +} + func TestMaxDebitZeroDefinedRate(t *testing.T) { ap, _ := ratingStorage.GetActionPlans("TOPUP10_AT") for _, at := range ap { diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index f988faadb..f3a85049a 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -160,6 +160,7 @@ 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 ` actionTimings = ` MORE_MINUTES,MINI,ONE_TIME_RUN,10 @@ -169,6 +170,7 @@ TOPUP10_AT,TOPUP10_AC1,*asap,10 TOPUP_SHARED0_AT,SE0,*asap,10 TOPUP_SHARED10_AT,SE10,*asap,10 TOPUP_EMPTY_AT,EE0,*asap,10 +POST_AT,NEG,*asap,10 ` actionTriggers = ` @@ -190,6 +192,7 @@ vdf,empty0,*out,TOPUP_SHARED0_AT, vdf,empty10,*out,TOPUP_SHARED10_AT, vdf,emptyX,*out,TOPUP_EMPTY_AT, vdf,emptyY,*out,TOPUP_EMPTY_AT, +vdf,post,*out,POST_AT, ` derivedCharges = ` @@ -765,8 +768,8 @@ func TestLoadRatingProfiles(t *testing.T) { } func TestLoadActions(t *testing.T) { - if len(csvr.actions) != 8 { - t.Error("Failed to load actions: ", csvr.actions) + if len(csvr.actions) != 9 { + t.Error("Failed to load actions: ", len(csvr.actions)) } as1 := csvr.actions["MINI"] expected := []*Action{ @@ -928,8 +931,8 @@ func TestLoadLCRs(t *testing.T) { } func TestLoadActionTimings(t *testing.T) { - if len(csvr.actionsTimings) != 5 { - t.Error("Failed to load action timings: ", csvr.actionsTimings) + if len(csvr.actionsTimings) != 6 { + t.Error("Failed to load action timings: ", len(csvr.actionsTimings)) } atm := csvr.actionsTimings["MORE_MINUTES"][0] expected := &ActionPlan{ @@ -990,8 +993,8 @@ func TestLoadActionTriggers(t *testing.T) { } func TestLoadAccountActions(t *testing.T) { - if len(csvr.accountActions) != 6 { - t.Error("Failed to load account actions: ", csvr.accountActions) + if len(csvr.accountActions) != 7 { + t.Error("Failed to load account actions: ", len(csvr.accountActions)) } aa := csvr.accountActions["*out:vdf:minitsboy"] expected := &Account{ From ecc7af8acbd0aae424a6013cbd4eba5d61421d61 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 18 Jun 2015 14:43:27 +0300 Subject: [PATCH 3/7] guard call cost log duplicate check --- engine/stats.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/stats.go b/engine/stats.go index 512cd4646..c6482f6b4 100644 --- a/engine/stats.go +++ b/engine/stats.go @@ -135,7 +135,7 @@ func (s *Stats) ResetQueues(ids []string, out *int) error { return nil } -// change the xisting ones +// change the existing ones // add new ones // delete the ones missing from the new list func (s *Stats) UpdateQueues(css []*CdrStats, out *int) error { From 908ced9b2ded6bbcea0f143e59ec89a6f54c8c90 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 18 Jun 2015 15:08:27 +0300 Subject: [PATCH 4/7] the real guardian of duplicate call info log --- engine/cdrs.go | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/engine/cdrs.go b/engine/cdrs.go index e8e06aac6..2fce079d7 100644 --- a/engine/cdrs.go +++ b/engine/cdrs.go @@ -22,7 +22,6 @@ import ( "fmt" "io/ioutil" "net/http" - "sync" "time" "github.com/cgrates/cgrates/config" @@ -62,7 +61,7 @@ func fsCdrHandler(w http.ResponseWriter, r *http.Request) { } func NewCdrServer(cgrCfg *config.CGRConfig, cdrDb CdrStorage, rater Connector, stats StatsInterface) (*CdrServer, error) { - return &CdrServer{cgrCfg: cgrCfg, cdrDb: cdrDb, rater: rater, stats: stats, callCostMutex: new(sync.RWMutex)}, nil + return &CdrServer{cgrCfg: cgrCfg, cdrDb: cdrDb, rater: rater, stats: stats, guard: &AccountLock{queue: make(map[string]chan bool)}}, nil /* if cfg.CDRSStats != "" { if cfg.CDRSStats != utils.INTERNAL { @@ -80,11 +79,11 @@ func NewCdrServer(cgrCfg *config.CGRConfig, cdrDb CdrStorage, rater Connector, s } type CdrServer struct { - cgrCfg *config.CGRConfig - cdrDb CdrStorage - rater Connector - stats StatsInterface - callCostMutex *sync.RWMutex + cgrCfg *config.CGRConfig + cdrDb CdrStorage + rater Connector + stats StatsInterface + guard *AccountLock } func (self *CdrServer) RegisterHanlersToServer(server *Server) { @@ -118,15 +117,17 @@ type CallCostLog struct { // RPC method, used to log callcosts to db func (self *CdrServer) LogCallCost(ccl *CallCostLog) error { if ccl.CheckDuplicate { - self.callCostMutex.Lock() // Avoid writing between checkDuplicate and logCallCost - defer self.callCostMutex.Unlock() - cc, err := self.cdrDb.GetCallCostLog(ccl.CgrId, ccl.Source, ccl.RunId) - if err != nil && err != gorm.RecordNotFound { - return err - } - if cc != nil { - return utils.ErrExists - } + _, err := self.guard.Guard(func() (interface{}, error) { + cc, err := self.cdrDb.GetCallCostLog(ccl.CgrId, ccl.Source, ccl.RunId) + if err != nil && err != gorm.RecordNotFound { + return nil, err + } + if cc != nil { + return nil, utils.ErrExists + } + return nil, self.cdrDb.LogCallCost(ccl.CgrId, ccl.Source, ccl.RunId, ccl.CallCost) + }, ccl.RunId) + return err } return self.cdrDb.LogCallCost(ccl.CgrId, ccl.Source, ccl.RunId, ccl.CallCost) } From be99e08cd2943795ead0189c99d952a7b75e273d Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 18 Jun 2015 15:40:31 +0300 Subject: [PATCH 5/7] guarding fix --- engine/cdrs.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/cdrs.go b/engine/cdrs.go index 2fce079d7..0ae246f4f 100644 --- a/engine/cdrs.go +++ b/engine/cdrs.go @@ -126,7 +126,7 @@ func (self *CdrServer) LogCallCost(ccl *CallCostLog) error { return nil, utils.ErrExists } return nil, self.cdrDb.LogCallCost(ccl.CgrId, ccl.Source, ccl.RunId, ccl.CallCost) - }, ccl.RunId) + }, ccl.CgrId) return err } return self.cdrDb.LogCallCost(ccl.CgrId, ccl.Source, ccl.RunId, ccl.CallCost) From 8f8da4af369a6539e514c6a6d6c67b27f67171d1 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 18 Jun 2015 16:57:37 +0300 Subject: [PATCH 6/7] improvements on negative charging --- engine/account.go | 25 ++++---- engine/account_test.go | 128 ---------------------------------------- engine/calldesc.go | 3 + engine/calldesc_test.go | 7 +-- 4 files changed, 21 insertions(+), 142 deletions(-) diff --git a/engine/account.go b/engine/account.go index 46241aa1f..d3f88b126 100644 --- a/engine/account.go +++ b/engine/account.go @@ -150,7 +150,7 @@ func (ub *Account) debitBalanceAction(a *Action, reset bool) error { func (ub *Account) getBalancesForPrefix(prefix, category string, balances BalanceChain, sharedGroup string) BalanceChain { var usefulBalances BalanceChain for _, b := range balances { - if b.IsExpired() || (ub.AllowNegative == false && b.SharedGroup == "" && b.Value <= 0) { + if b.IsExpired() || (b.SharedGroup == "" && b.Value <= 0) { continue } if sharedGroup != "" && b.SharedGroup != sharedGroup { @@ -320,18 +320,23 @@ func (ub *Account) debitCreditBalance(cd *CallDescriptor, count bool, dryRun boo if err != nil { Logger.Err(fmt.Sprintf("Error getting new cost for balance subject: %v", err)) } - initialLength = len(cc.Timespans) - cc.Timespans = append(cc.Timespans, leftCC.Timespans...) - if initialLength == 0 { - // this is the first add, debit the connect fee - ub.DebitConnectionFee(cc, usefulMoneyBalances, count) + if leftCC.Cost == 0 && len(leftCC.Timespans) > 0 { + cc.Timespans = append(cc.Timespans, leftCC.Timespans...) } - if leftCC.Cost == 0 || goNegative { - //log.Printf("Left CC: %+v", leftCC) + + //log.Printf("HERE: %+v %d", leftCC) + if leftCC.Cost > 0 && goNegative { + initialLength = len(cc.Timespans) + cc.Timespans = append(cc.Timespans, leftCC.Timespans...) + if initialLength == 0 { + // this is the first add, debit the connect fee + ub.DebitConnectionFee(cc, usefulMoneyBalances, count) + } + //log.Printf("Left CC: %+v ", leftCC) // get the default money balanance // and go negative on it with the amount still unpaid - if len(leftCC.Timespans) > 0 && leftCC.Cost > 0 && !ub.AllowNegative { - err = errors.New("not enough credit") + if len(leftCC.Timespans) > 0 && leftCC.Cost > 0 && !ub.AllowNegative && !dryRun { + Logger.Err(fmt.Sprintf(" Going negative on account %s with AllowNegative: false", cd.GetAccountKey())) } for _, ts := range leftCC.Timespans { if ts.Increments == nil { diff --git a/engine/account_test.go b/engine/account_test.go index b00acdbdc..22d0e585b 100644 --- a/engine/account_test.go +++ b/engine/account_test.go @@ -803,134 +803,6 @@ func TestDebitCreditSubjectMoney(t *testing.T) { } }*/ -func TestDebitCreditSubjectMixedMoreTS(t *testing.T) { - b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationIds: "NAT", RatingSubject: "minu"} - cc := &CallCost{ - Tenant: "vdf", - Category: "0", - Direction: OUTBOUND, - Destination: "0723045326", - Timespans: []*TimeSpan{ - &TimeSpan{ - TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC), - TimeEnd: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC), - DurationIndex: 0, - RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, - }, - &TimeSpan{ - TimeStart: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC), - TimeEnd: time.Date(2013, 9, 24, 10, 49, 20, 0, time.UTC), - DurationIndex: 10 * time.Second, - RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 1, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, - }, - }, - TOR: utils.VOICE, - deductConnectFee: true, - } - cd := &CallDescriptor{ - Tenant: cc.Tenant, - Category: cc.Category, - TimeStart: cc.Timespans[0].TimeStart, - TimeEnd: cc.Timespans[1].TimeEnd, - Direction: cc.Direction, - Destination: cc.Destination, - TOR: cc.TOR, - DurationIndex: cc.GetDuration(), - testCallcost: cc, - } - rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ - utils.VOICE + OUTBOUND: BalanceChain{b1}, - utils.MONETARY + OUTBOUND: BalanceChain{&Balance{Uuid: "moneya", Value: 50, RatingSubject: "minu"}}, - }} - var err error - cc, err = rifsBalance.debitCreditBalance(cd, false, false, true) - if err == nil { - t.Error("Error showing debiting balance error: ", err) - } - //t.Logf("%+v %+v", cc.Timespans[0], cc.Timespans[1]) - /*if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "testb" || - cc.Timespans[0].Increments[0].Duration != time.Second { - t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0]) - } - if rifsBalance.BalanceMap[utils.VOICE+OUTBOUND][0].Value != 20 || - rifsBalance.BalanceMap[utils.MONETARY+OUTBOUND][0].Value != 0 || - rifsBalance.BalanceMap[utils.MONETARY+OUTBOUND][1].Value != -31 { - t.Errorf("Error extracting minutes from balance: %+v, %+v, %+v", - rifsBalance.BalanceMap[utils.VOICE+OUTBOUND][0].Value, rifsBalance.BalanceMap[utils.MONETARY+OUTBOUND][0].Value, - rifsBalance.BalanceMap[utils.MONETARY+OUTBOUND][1].Value) - } - if len(cc.Timespans) != 2 || cc.Timespans[0].GetDuration() != 50*time.Second || cc.Timespans[1].GetDuration() != 30*time.Second { - for _, ts := range cc.Timespans { - t.Log(ts.GetDuration()) - } - t.Error("Error truncating extra timespans: ", len(cc.Timespans)) - }*/ -} - -func TestDebitCreditSubjectMixedPartPay(t *testing.T) { - b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationIds: "NAT", RatingSubject: "minu"} - cc := &CallCost{ - Tenant: "vdf", - Category: "0", - Direction: OUTBOUND, - Destination: "0723045326", - Timespans: []*TimeSpan{ - &TimeSpan{ - TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC), - TimeEnd: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC), - DurationIndex: 0, - RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, - }, - &TimeSpan{ - TimeStart: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC), - TimeEnd: time.Date(2013, 9, 24, 10, 49, 20, 0, time.UTC), - DurationIndex: 10 * time.Second, - RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 1, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, - }, - }, - TOR: utils.VOICE, - deductConnectFee: true, - } - cd := &CallDescriptor{ - Tenant: cc.Tenant, - Category: cc.Category, - TimeStart: cc.Timespans[0].TimeStart, - TimeEnd: cc.Timespans[1].TimeEnd, - Direction: cc.Direction, - Destination: cc.Destination, - TOR: cc.TOR, - DurationIndex: cc.GetDuration(), - testCallcost: cc, - } - rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ - utils.VOICE + OUTBOUND: BalanceChain{b1}, - utils.MONETARY + OUTBOUND: BalanceChain{&Balance{Uuid: "moneya", Value: 75, RatingSubject: "minu"}}, - }} - var err error - cc, err = rifsBalance.debitCreditBalance(cd, false, false, true) - if err == nil { - t.Error("Error showing debiting balance error: ", err) - } - //t.Logf("%+v %+v", cc.Timespans[0], cc.Timespans[1]) - /*if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "testb" || - cc.Timespans[0].Increments[0].Duration != time.Second { - t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0]) - } - if rifsBalance.BalanceMap[utils.VOICE+OUTBOUND][0].Value != 0 || - rifsBalance.BalanceMap[utils.MONETARY+OUTBOUND][0].Value != 0 || - rifsBalance.BalanceMap[utils.MONETARY+OUTBOUND][1].Value != -11 { - t.Errorf("Error extracting minutes from balance: %+v, %+v %+v", - rifsBalance.BalanceMap[utils.VOICE+OUTBOUND][0].Value, rifsBalance.BalanceMap[utils.MONETARY+OUTBOUND][0].Value, - rifsBalance.BalanceMap[utils.MONETARY+OUTBOUND][1].Value) - } - if len(cc.Timespans) != 3 || cc.Timespans[0].GetDuration() != 70*time.Second || cc.Timespans[1].GetDuration() != 5*time.Second || cc.Timespans[2].GetDuration() != 5*time.Second { - for _, ts := range cc.Timespans { - t.Log(ts.GetDuration()) - } - t.Error("Error truncating extra timespans: ", len(cc.Timespans)) - }*/ -} - func TestAccountdebitBalance(t *testing.T) { ub := &Account{ Id: "rif", diff --git a/engine/calldesc.go b/engine/calldesc.go index 60e82a074..01ba1da85 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -525,6 +525,9 @@ func (origCD *CallDescriptor) getMaxSessionDuration(origAcc *Account) (time.Dura return utils.MinDuration(initialDuration, totalDuration), nil } } + if ts.Increments == nil { + ts.createIncrementsSlice() + } for _, incr := range ts.Increments { totalCost += incr.Cost if defaultBalance.Value < 0 && incr.BalanceInfo.MoneyBalanceUuid == defaultBalance.Uuid { diff --git a/engine/calldesc_test.go b/engine/calldesc_test.go index 2e3d77fdb..e9423bc0b 100644 --- a/engine/calldesc_test.go +++ b/engine/calldesc_test.go @@ -828,7 +828,7 @@ func TestMaxSesionTimeEmptyBalance(t *testing.T) { acc, _ := accountingStorage.GetAccount("*out:vdf:luna") allowedTime, err := cd.getMaxSessionDuration(acc) if err != nil || allowedTime != 0 { - t.Error("Error get max session for 0 acount") + t.Error("Error get max session for 0 acount", err) } } @@ -846,7 +846,7 @@ func TestMaxSesionTimeEmptyBalanceAndNoCost(t *testing.T) { acc, _ := accountingStorage.GetAccount("*out:vdf:luna") allowedTime, err := cd.getMaxSessionDuration(acc) if err != nil || allowedTime == 0 { - t.Error("Error get max session for 0 acount") + t.Error("Error get max session for 0 acount", err) } } @@ -906,7 +906,7 @@ func TestDebitFromEmptyShare(t *testing.T) { } } -func TestDebitNegative(t *testing.T) { +func TestDebitNegatve(t *testing.T) { ap, _ := ratingStorage.GetActionPlans("POST_AT") for _, at := range ap { at.Execute() @@ -922,7 +922,6 @@ func TestDebitNegative(t *testing.T) { Account: "post", Destination: "0723", } - cc, err := cd.MaxDebit() //utils.PrintFull(cc) if err != nil || cc.Cost != 2.5 { From 14a225e26908e2a6953c96ae792f086f366fb5e0 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 18 Jun 2015 17:14:30 +0300 Subject: [PATCH 7/7] possible fix for #99 --- apier/v1/accounts.go | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index b8d9920d1..058bd9b04 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -134,17 +134,14 @@ func (self *ApierV1) RemAccountActionTriggers(attrs AttrRemAcntActionTriggers, r if err != nil { return 0, err } - for idx, actr := range ub.ActionTriggers { + nactrs := make(engine.ActionTriggerPriotityList, 0) + for _, actr := range ub.ActionTriggers { match, _ := regexp.MatchString(attrs.ActionTriggersId, actr.Id) if len(attrs.ActionTriggersId) != 0 && !match { - continue - } - if len(ub.ActionTriggers) != 1 { // Remove by index - ub.ActionTriggers[idx], ub.ActionTriggers = ub.ActionTriggers[len(ub.ActionTriggers)-1], ub.ActionTriggers[:len(ub.ActionTriggers)-1] - } else { // For last item, simply reinit the slice - ub.ActionTriggers = make(engine.ActionTriggerPriotityList, 0) + nactrs = append(nactrs, actr) } } + ub.ActionTriggers = nactrs if err := self.AccountDb.SetAccount(ub); err != nil { return 0, err }