func GetUB() *Account { uc := &UnitCounter{ BalanceType: utils.SMS, Balances: BalanceChain{&Balance{Value: 1}, &Balance{Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}, } at := &ActionTrigger{ Id: "some_uuid", BalanceType: utils.MONETARY, BalanceDirections: utils.NewStringMap(utils.OUT), ThresholdValue: 100.0, BalanceDestinationIds: utils.NewStringMap("NAT"), Weight: 10.0, ActionsId: "Commando", } var zeroTime time.Time zeroTime = zeroTime.UTC() // for deep equal to find location ub := &Account{ Id: "rif", AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.SMS: BalanceChain{&Balance{Value: 14, ExpirationDate: zeroTime}}, utils.DATA: BalanceChain{&Balance{Value: 1024, ExpirationDate: zeroTime}}, utils.VOICE: BalanceChain{&Balance{Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{uc, uc}, ActionTriggers: ActionTriggers{at, at, at}, } return ub }
func GetUB() *Account { uc := &UnitCounter{ Counters: CounterFilters{&CounterFilter{Value: 1}, &CounterFilter{Filter: &BalanceFilter{Weight: utils.Float64Pointer(20), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT"))}}, &CounterFilter{Filter: &BalanceFilter{Weight: utils.Float64Pointer(10), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET"))}}}, } at := &ActionTrigger{ ID: "some_uuid", ThresholdValue: 100.0, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), }, Weight: 10.0, ActionsID: "Commando", } var zeroTime time.Time zeroTime = zeroTime.UTC() // for deep equal to find location ub := &Account{ ID: "rif", AllowNegative: true, BalanceMap: map[string]Balances{utils.SMS: Balances{&Balance{Value: 14, ExpirationDate: zeroTime}}, utils.DATA: Balances{&Balance{Value: 1024, ExpirationDate: zeroTime}}, utils.VOICE: Balances{&Balance{Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.SMS: []*UnitCounter{uc, uc}}, ActionTriggers: ActionTriggers{at, at, at}, } return ub }
func testOnStorITCacheActions(t *testing.T) { acts := Actions{ &Action{ Id: "MINI", ActionType: TOPUP_RESET, ExpirationString: UNLIMITED, Weight: 10, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Uuid: utils.StringPointer(utils.GenUUID()), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Value: &utils.ValueFormula{Static: 10, Params: make(map[string]interface{})}, Weight: utils.Float64Pointer(10), Disabled: utils.BoolPointer(false), Timings: make([]*RITiming, 0), Blocker: utils.BoolPointer(false), }, }, &Action{ Id: "MINI", ActionType: TOPUP, ExpirationString: UNLIMITED, Weight: 10, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Uuid: utils.StringPointer(utils.GenUUID()), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Value: &utils.ValueFormula{Static: 100, Params: make(map[string]interface{})}, Weight: utils.Float64Pointer(10), RatingSubject: utils.StringPointer("test"), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), Disabled: utils.BoolPointer(false), Timings: make([]*RITiming, 0), Blocker: utils.BoolPointer(false), }, }, } if err := onStor.SetActions(acts[0].Id, acts, utils.NonTransactional); err != nil { t.Error(err) } if _, hasIt := cache.Get(utils.ACTION_PREFIX + acts[0].Id); hasIt { t.Error("Already in cache") } if err := onStor.CacheDataFromDB(utils.ACTION_PREFIX, []string{acts[0].Id}, false); err != nil { t.Error(err) } if itm, hasIt := cache.Get(utils.ACTION_PREFIX + acts[0].Id); !hasIt { t.Error("Did not cache") } else if rcv := itm.(Actions); !reflect.DeepEqual(acts, rcv) { t.Errorf("Expecting: %+v, received: %+v", acts, rcv) } }
func TestLoadAccountActions(t *testing.T) { if len(csvr.accountActions) != 17 { t.Error("Failed to load account actions: ", len(csvr.accountActions)) } aa := csvr.accountActions["vdf:minitsboy"] expected := &Account{ ID: "vdf:minitsboy", UnitCounters: UnitCounters{ utils.VOICE: []*UnitCounter{ &UnitCounter{ CounterType: "*event", Counters: CounterFilters{ &CounterFilter{ Value: 0, Filter: &BalanceFilter{ ID: utils.StringPointer("st0"), Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap("*out")), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("GERMANY_O2")), SharedGroups: nil, Categories: nil, TimingIDs: nil, }, }, }, }, }, }, ActionTriggers: csvr.actionsTriggers["STANDARD_TRIGGER"], } // set propper uuid for i, atr := range aa.ActionTriggers { csvr.actionsTriggers["STANDARD_TRIGGER"][i].ID = atr.ID } for i, b := range aa.UnitCounters[utils.VOICE][0].Counters { expected.UnitCounters[utils.VOICE][0].Counters[i].Filter.ID = b.Filter.ID } if !reflect.DeepEqual(aa.UnitCounters[utils.VOICE][0].Counters[0], expected.UnitCounters[utils.VOICE][0].Counters[0]) { t.Errorf("Error loading account action: %+v", utils.ToIJSON(aa.UnitCounters[utils.VOICE][0].Counters[0].Filter)) } // test that it does not overwrite balances existing, err := accountingStorage.GetAccount(aa.ID) if err != nil || len(existing.BalanceMap) != 2 { t.Errorf("The account was not set before load: %+v", existing) } accountingStorage.SetAccount(aa) existing, err = accountingStorage.GetAccount(aa.ID) if err != nil || len(existing.BalanceMap) != 2 { t.Errorf("The set account altered the balances: %+v", existing) } }
func TestLoadActionTriggers(t *testing.T) { if len(csvr.actionsTriggers) != 7 { t.Error("Failed to load action triggers: ", len(csvr.actionsTriggers)) } atr := csvr.actionsTriggers["STANDARD_TRIGGER"][0] expected := &ActionTrigger{ ID: "STANDARD_TRIGGER", UniqueID: "st0", ThresholdType: utils.TRIGGER_MIN_EVENT_COUNTER, ThresholdValue: 10, Balance: &BalanceFilter{ ID: nil, Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("GERMANY_O2")), Categories: nil, TimingIDs: nil, SharedGroups: nil, Disabled: nil, Blocker: nil, }, Weight: 10, ActionsID: "SOME_1", Executed: false, } if !reflect.DeepEqual(atr, expected) { t.Errorf("Error loading action trigger: %+v", utils.ToIJSON(atr.Balance)) } atr = csvr.actionsTriggers["STANDARD_TRIGGER"][1] expected = &ActionTrigger{ ID: "STANDARD_TRIGGER", UniqueID: "st1", ThresholdType: utils.TRIGGER_MAX_BALANCE, ThresholdValue: 200, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("GERMANY")), Categories: nil, TimingIDs: nil, SharedGroups: nil, }, Weight: 10, ActionsID: "SOME_2", Executed: false, } if !reflect.DeepEqual(atr, expected) { t.Errorf("Error loading action trigger: %+v", atr) } }
func TestBalanceClone(t *testing.T) { mb1 := &Balance{Value: 1, Weight: 2, RatingSubject: "test", DestinationIds: utils.NewStringMap("5")} mb2 := mb1.Clone() if mb1 == mb2 || !mb1.Equal(mb2) { t.Errorf("Cloning failure: \n%+v\n%+v", mb1, mb2) } }
func TestLoadAccountActions(t *testing.T) { if len(csvr.accountActions) != 11 { t.Error("Failed to load account actions: ", len(csvr.accountActions)) } aa := csvr.accountActions["vdf:minitsboy"] expected := &Account{ Id: "vdf:minitsboy", UnitCounters: UnitCounters{ &UnitCounter{ BalanceType: "*voice", CounterType: "*event", Balances: BalanceChain{ &Balance{ Id: "2c2ce3c9-d62b-49dc-82a5-2a17bdc6eb4e", Value: 0, Directions: utils.NewStringMap("*out"), DestinationIds: utils.NewStringMap("GERMANY_O2"), SharedGroups: utils.StringMap{}, Categories: utils.StringMap{}, TimingIDs: utils.StringMap{}, }, }, }, }, ActionTriggers: csvr.actionsTriggers["STANDARD_TRIGGER"], } // set propper uuid for i, atr := range aa.ActionTriggers { csvr.actionsTriggers["STANDARD_TRIGGER"][i].Id = atr.Id } for i, b := range aa.UnitCounters[0].Balances { expected.UnitCounters[0].Balances[i].Id = b.Id } if !reflect.DeepEqual(aa.UnitCounters[0].Balances[0], expected.UnitCounters[0].Balances[0]) { t.Errorf("Error loading account action: %+v \n %+v", aa.UnitCounters[0].Balances[0], expected.UnitCounters[0].Balances[0]) } // test that it does not overwrite balances existing, err := accountingStorage.GetAccount(aa.Id) if err != nil || len(existing.BalanceMap) != 2 { t.Errorf("The account was not set before load: %+v", existing) } accountingStorage.SetAccount(aa) existing, err = accountingStorage.GetAccount(aa.Id) if err != nil || len(existing.BalanceMap) != 2 { t.Errorf("The set account altered the balances: %+v", existing) } }
func TestHandleDeivedChargersMatchDestNegativeSpec(t *testing.T) { dcs := &utils.DerivedChargers{ DestinationIDs: utils.NewStringMap("NAT", "!SPEC"), } if DerivedChargersMatchesDest(dcs, "0723045326") { t.Error("Derived charger failed to match dest") } }
func TestHandleDeivedChargersMatchDestRet(t *testing.T) { dcs := &utils.DerivedChargers{ DestinationIDs: utils.NewStringMap("RET"), } if !DerivedChargersMatchesDest(dcs, "0723045326") { t.Error("Derived charger failed to match dest") } }
func TestLoadActionTriggers(t *testing.T) { if len(csvr.actionsTriggers) != 7 { t.Error("Failed to load action triggers: ", len(csvr.actionsTriggers)) } atr := csvr.actionsTriggers["STANDARD_TRIGGER"][0] expected := &ActionTrigger{ ID: "STANDARD_TRIGGER", UniqueID: "st0", BalanceType: utils.VOICE, BalanceDirections: utils.NewStringMap(utils.OUT), ThresholdType: utils.TRIGGER_MIN_EVENT_COUNTER, ThresholdValue: 10, BalanceDestinationIds: utils.NewStringMap("GERMANY_O2"), BalanceCategories: utils.StringMap{}, BalanceTimingTags: utils.StringMap{}, BalanceSharedGroups: utils.StringMap{}, Weight: 10, ActionsId: "SOME_1", Executed: false, } if !reflect.DeepEqual(atr, expected) { t.Errorf("Error loading action trigger: %+v", atr) } atr = csvr.actionsTriggers["STANDARD_TRIGGER"][1] expected = &ActionTrigger{ ID: "STANDARD_TRIGGER", UniqueID: "st1", BalanceType: utils.VOICE, BalanceDirections: utils.NewStringMap(utils.OUT), ThresholdType: utils.TRIGGER_MAX_BALANCE, ThresholdValue: 200, BalanceDestinationIds: utils.NewStringMap("GERMANY"), BalanceCategories: utils.StringMap{}, BalanceTimingTags: utils.StringMap{}, BalanceSharedGroups: utils.StringMap{}, Weight: 10, ActionsId: "SOME_2", Executed: false, } if !reflect.DeepEqual(atr, expected) { t.Errorf("Error loading action trigger: %+v", atr) } }
func TestUnitsCounterAddBalance(t *testing.T) { uc := &UnitCounter{ BalanceType: utils.SMS, Balances: BalanceChain{&Balance{Value: 1}, &Balance{Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}, } UnitCounters{uc}.addUnits(20, utils.SMS, &CallCost{Destination: "test"}, nil) if len(uc.Balances) != 3 { t.Error("Error adding minute bucket: ", uc.Balances) } }
func TestBalanceMatchActionTriggerSharedGroup(t *testing.T) { at := &ActionTrigger{Balance: &BalanceFilter{SharedGroups: utils.StringMapPointer(utils.NewStringMap("test"))}} b := &Balance{SharedGroups: utils.NewStringMap("test")} if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } b.SharedGroups = utils.NewStringMap("test1") if b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } b.SharedGroups = utils.NewStringMap("") if b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } b.SharedGroups = utils.NewStringMap("test") at.Balance.SharedGroups = nil if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } }
func TestGetDC(t *testing.T) { attrs := utils.AttrDerivedChargers{Tenant: "cgrates.org", Category: "call", Direction: "*out", Account: "dan", Subject: "dan"} eDcs := utils.DerivedChargers{DestinationIds: utils.NewStringMap(), Chargers: []*utils.DerivedCharger{ &utils.DerivedCharger{RunId: "extra1", ReqTypeField: "^prepaid", DirectionField: "*default", TenantField: "*default", CategoryField: "*default", AccountField: "rif", SubjectField: "rif", DestinationField: "*default", SetupTimeField: "*default", AnswerTimeField: "*default", UsageField: "*default"}, &utils.DerivedCharger{RunId: "extra2", ReqTypeField: "*default", DirectionField: "*default", TenantField: "*default", CategoryField: "*default", AccountField: "ivo", SubjectField: "ivo", DestinationField: "*default", SetupTimeField: "*default", AnswerTimeField: "*default", UsageField: "*default"}, }} var dcs utils.DerivedChargers if err := apierDcT.GetDerivedChargers(attrs, &dcs); err != nil { t.Error("Unexpected error", err.Error()) } else if !reflect.DeepEqual(dcs, eDcs) { t.Errorf("Expecting: %v, received: %v", eDcs.DestinationIds, dcs.DestinationIds) } }
func (self *ApierV1) SetDerivedChargers(attrs AttrSetDerivedChargers, reply *string) (err error) { if len(attrs.DerivedChargers) == 0 { return utils.NewErrMandatoryIeMissing("DerivedChargers") } if len(attrs.Direction) == 0 { attrs.Direction = utils.OUT } if len(attrs.Tenant) == 0 { attrs.Tenant = utils.ANY } if len(attrs.Category) == 0 { attrs.Category = utils.ANY } if len(attrs.Account) == 0 { attrs.Account = utils.ANY } if len(attrs.Subject) == 0 { attrs.Subject = utils.ANY } for _, dc := range attrs.DerivedChargers { if _, err = utils.ParseRSRFields(dc.RunFilters, utils.INFIELD_SEP); err != nil { // Make sure rules are OK before loading in db return fmt.Errorf("%s:%s", utils.ErrParserError.Error(), err.Error()) } } dcKey := utils.DerivedChargersKey(attrs.Direction, attrs.Tenant, attrs.Category, attrs.Account, attrs.Subject) if !attrs.Overwrite { if exists, err := self.RatingDb.HasData(utils.DERIVEDCHARGERS_PREFIX, dcKey); err != nil { return utils.NewErrServerError(err) } else if exists { return utils.ErrExists } } dstIds := strings.Split(attrs.DestinationIds, utils.INFIELD_SEP) dcs := &utils.DerivedChargers{DestinationIDs: utils.NewStringMap(dstIds...), Chargers: attrs.DerivedChargers} if err := self.RatingDb.SetDerivedChargers(dcKey, dcs); err != nil { return utils.NewErrServerError(err) } if err := self.RatingDb.CacheRatingPrefixValues(map[string][]string{ utils.DERIVEDCHARGERS_PREFIX: []string{utils.DERIVEDCHARGERS_PREFIX + dcKey}, }); err != nil { return utils.NewErrServerError(err) } *reply = utils.OK return nil }
func TestSharedSetGet(t *testing.T) { id := "TEST_SG100" sg := &SharedGroup{ Id: id, AccountParameters: map[string]*SharingParameters{ "test": &SharingParameters{Strategy: STRATEGY_HIGHEST}, }, MemberIds: utils.NewStringMap("1", "2", "3"), } err := ratingStorage.SetSharedGroup(sg, utils.NonTransactional) if err != nil { t.Error("Error storing Shared groudp: ", err) } received, err := ratingStorage.GetSharedGroup(id, true, utils.NonTransactional) if err != nil || received == nil || !reflect.DeepEqual(sg, received) { t.Error("Error getting shared group: ", err, received) } received, err = ratingStorage.GetSharedGroup(id, false, utils.NonTransactional) if err != nil || received == nil || !reflect.DeepEqual(sg, received) { t.Error("Error getting cached shared group: ", err, received) } }
func TestBalanceMatchActionTriggerDestination(t *testing.T) { at := &ActionTrigger{BalanceDestinationIds: utils.NewStringMap("test")} b := &Balance{DestinationIds: utils.NewStringMap("test")} if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } b.DestinationIds = utils.NewStringMap("test1") if b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } b.DestinationIds = utils.NewStringMap("") if b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } b.DestinationIds = utils.NewStringMap("test") at.BalanceDestinationIds = utils.NewStringMap("") if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } }
func TestUnitsCounterAddBalanceExists(t *testing.T) { uc := &UnitCounter{ Counters: CounterFilters{&CounterFilter{Value: 1}, &CounterFilter{Value: 10, Filter: &BalanceFilter{Weight: utils.Float64Pointer(20), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT"))}}, &CounterFilter{Filter: &BalanceFilter{Weight: utils.Float64Pointer(10), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET"))}}}, } UnitCounters{utils.SMS: []*UnitCounter{uc}}.addUnits(5, utils.SMS, &CallCost{Destination: "0723"}, nil) if len(uc.Counters) != 3 || uc.Counters[1].Value != 15 { t.Error("Error adding minute bucket!") } }
func (self *ApierV1) SetActionTrigger(attr AttrSetActionTrigger, reply *string) error { if missing := utils.MissingStructFields(&attr, []string{"GroupID"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } atrs, _ := self.RatingDb.GetActionTriggers(attr.GroupID, false, utils.NonTransactional) var newAtr *engine.ActionTrigger if attr.UniqueID != "" { //search for exiting one for _, atr := range atrs { if atr.UniqueID == attr.UniqueID { newAtr = atr break } } } if newAtr == nil { newAtr = &engine.ActionTrigger{} atrs = append(atrs, newAtr) } newAtr.ID = attr.GroupID if attr.UniqueID != "" { newAtr.UniqueID = attr.UniqueID } else { newAtr.UniqueID = utils.GenUUID() } if attr.ThresholdType != nil { newAtr.ThresholdType = *attr.ThresholdType } if attr.ThresholdValue != nil { newAtr.ThresholdValue = *attr.ThresholdValue } if attr.Recurrent != nil { newAtr.Recurrent = *attr.Recurrent } if attr.MinSleep != nil { minSleep, err := utils.ParseDurationWithSecs(*attr.MinSleep) if err != nil { *reply = err.Error() return err } newAtr.MinSleep = minSleep } if attr.ExpirationDate != nil { expTime, err := utils.ParseTimeDetectLayout(*attr.ExpirationDate, self.Config.DefaultTimezone) if err != nil { *reply = err.Error() return err } newAtr.ExpirationDate = expTime } if attr.ActivationDate != nil { actTime, err := utils.ParseTimeDetectLayout(*attr.ActivationDate, self.Config.DefaultTimezone) if err != nil { *reply = err.Error() return err } newAtr.ActivationDate = actTime } newAtr.Balance = &engine.BalanceFilter{} if attr.BalanceID != nil { newAtr.Balance.ID = attr.BalanceID } if attr.BalanceType != nil { newAtr.Balance.Type = attr.BalanceType } if attr.BalanceDirections != nil { newAtr.Balance.Directions = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceDirections...)) } if attr.BalanceDestinationIds != nil { newAtr.Balance.DestinationIDs = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceDestinationIds...)) } if attr.BalanceWeight != nil { newAtr.Balance.Weight = attr.BalanceWeight } if attr.BalanceExpirationDate != nil { balanceExpTime, err := utils.ParseDate(*attr.BalanceExpirationDate) if err != nil { *reply = err.Error() return err } newAtr.Balance.ExpirationDate = &balanceExpTime } if attr.BalanceTimingTags != nil { newAtr.Balance.TimingIDs = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceTimingTags...)) } if attr.BalanceRatingSubject != nil { newAtr.Balance.RatingSubject = attr.BalanceRatingSubject } if attr.BalanceCategories != nil { newAtr.Balance.Categories = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceCategories...)) } if attr.BalanceSharedGroups != nil { newAtr.Balance.SharedGroups = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceSharedGroups...)) } if attr.BalanceBlocker != nil { newAtr.Balance.Blocker = attr.BalanceBlocker } if attr.BalanceDisabled != nil { newAtr.Balance.Disabled = attr.BalanceDisabled } if attr.MinQueuedItems != nil { newAtr.MinQueuedItems = *attr.MinQueuedItems } if attr.ActionsID != nil { newAtr.ActionsID = *attr.ActionsID } if err := self.RatingDb.SetActionTriggers(attr.GroupID, atrs, utils.NonTransactional); err != nil { *reply = err.Error() return err } //no cache for action triggers *reply = utils.OK return nil }
func (self *ApierV1) SetAccountActionTriggers(attr AttrSetAccountActionTriggers, reply *string) error { if missing := utils.MissingStructFields(&attr, []string{"Tenant", "Account"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } accID := utils.AccountKey(attr.Tenant, attr.Account) var account *engine.Account _, err := engine.Guardian.Guard(func() (interface{}, error) { if acc, err := self.AccountDb.GetAccount(accID); err == nil { account = acc } else { return 0, err } for _, at := range account.ActionTriggers { if (attr.UniqueID == "" || at.UniqueID == attr.UniqueID) && (attr.GroupID == "" || at.ID == attr.GroupID) { // we have a winner if attr.ThresholdType != nil { at.ThresholdType = *attr.ThresholdType } if attr.ThresholdValue != nil { at.ThresholdValue = *attr.ThresholdValue } if attr.Recurrent != nil { at.Recurrent = *attr.Recurrent } if attr.Executed != nil { at.Executed = *attr.Executed } if attr.MinSleep != nil { minSleep, err := utils.ParseDurationWithSecs(*attr.MinSleep) if err != nil { return 0, err } at.MinSleep = minSleep } if attr.ExpirationDate != nil { expTime, err := utils.ParseTimeDetectLayout(*attr.ExpirationDate, self.Config.DefaultTimezone) if err != nil { return 0, err } at.ExpirationDate = expTime } if attr.ActivationDate != nil { actTime, err := utils.ParseTimeDetectLayout(*attr.ActivationDate, self.Config.DefaultTimezone) if err != nil { return 0, err } at.ActivationDate = actTime } at.Balance = &engine.BalanceFilter{} if attr.BalanceID != nil { at.Balance.ID = attr.BalanceID } if attr.BalanceType != nil { at.Balance.Type = attr.BalanceType } if attr.BalanceDirections != nil { at.Balance.Directions = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceDirections...)) } if attr.BalanceDestinationIds != nil { at.Balance.DestinationIDs = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceDestinationIds...)) } if attr.BalanceWeight != nil { at.Balance.Weight = attr.BalanceWeight } if attr.BalanceExpirationDate != nil { balanceExpTime, err := utils.ParseDate(*attr.BalanceExpirationDate) if err != nil { return 0, err } at.Balance.ExpirationDate = &balanceExpTime } if attr.BalanceTimingTags != nil { at.Balance.TimingIDs = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceTimingTags...)) } if attr.BalanceRatingSubject != nil { at.Balance.RatingSubject = attr.BalanceRatingSubject } if attr.BalanceCategories != nil { at.Balance.Categories = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceCategories...)) } if attr.BalanceSharedGroups != nil { at.Balance.SharedGroups = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceSharedGroups...)) } if attr.BalanceBlocker != nil { at.Balance.Blocker = attr.BalanceBlocker } if attr.BalanceDisabled != nil { at.Balance.Disabled = attr.BalanceDisabled } if attr.MinQueuedItems != nil { at.MinQueuedItems = *attr.MinQueuedItems } if attr.ActionsID != nil { at.ActionsID = *attr.ActionsID } } } account.ExecuteActionTriggers(nil) if err := self.AccountDb.SetAccount(account); err != nil { return 0, err } return 0, nil }, 0, accID) if err != nil { *reply = err.Error() return err } *reply = utils.OK return nil }
func TestUnitCountersCountAllMonetary(t *testing.T) { a := &Account{ ActionTriggers: ActionTriggers{ &ActionTrigger{ UniqueID: "TestTR1", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), }, }, &ActionTrigger{ UniqueID: "TestTR11", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), }, }, &ActionTrigger{ UniqueID: "TestTR2", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), }, }, &ActionTrigger{ UniqueID: "TestTR3", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), }, }, &ActionTrigger{ UniqueID: "TestTR4", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), }, }, &ActionTrigger{ UniqueID: "TestTR5", ThresholdType: utils.TRIGGER_MAX_BALANCE, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), }, }, }, } a.InitCounters() a.UnitCounters.addUnits(10, utils.MONETARY, &CallCost{}, nil) if len(a.UnitCounters) != 3 || len(a.UnitCounters[utils.MONETARY][0].Counters) != 2 || a.UnitCounters[utils.MONETARY][0].Counters[0].Value != 10 || a.UnitCounters[utils.MONETARY][0].Counters[1].Value != 10 { for key, counters := range a.UnitCounters { t.Log(key) for _, uc := range counters { t.Logf("UC: %+v", uc) for _, b := range uc.Counters { t.Logf("B: %+v", b) } } } t.Errorf("Error Initializing adding unit counters: %v", len(a.UnitCounters)) } }
func TestUnitCountersKeepValuesAfterInit(t *testing.T) { a := &Account{ ActionTriggers: ActionTriggers{ &ActionTrigger{ UniqueID: "TestTR1", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, BalanceType: utils.MONETARY, BalanceDirections: utils.NewStringMap(utils.OUT), BalanceWeight: 10, }, &ActionTrigger{ UniqueID: "TestTR11", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, BalanceType: utils.MONETARY, BalanceDirections: utils.NewStringMap(utils.OUT), BalanceWeight: 20, }, &ActionTrigger{ UniqueID: "TestTR2", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, BalanceType: utils.VOICE, BalanceDirections: utils.NewStringMap(utils.OUT), BalanceDestinationIds: utils.NewStringMap("NAT"), BalanceWeight: 10, }, &ActionTrigger{ UniqueID: "TestTR22", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, BalanceType: utils.VOICE, BalanceDestinationIds: utils.NewStringMap("RET"), BalanceWeight: 10, }, &ActionTrigger{ UniqueID: "TestTR3", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, BalanceType: utils.VOICE, BalanceDirections: utils.NewStringMap(utils.OUT), BalanceWeight: 10, }, &ActionTrigger{ UniqueID: "TestTR4", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, BalanceType: utils.SMS, BalanceDirections: utils.NewStringMap(utils.OUT), BalanceWeight: 10, }, &ActionTrigger{ UniqueID: "TestTR5", ThresholdType: utils.TRIGGER_MAX_BALANCE, BalanceType: utils.SMS, BalanceDirections: utils.NewStringMap(utils.OUT), BalanceWeight: 10, }, }, } a.InitCounters() a.UnitCounters.addUnits(10, utils.VOICE, &CallCost{Destination: "0723045326"}, nil) if len(a.UnitCounters) != 4 || len(a.UnitCounters[1].Balances) != 2 || a.UnitCounters[1].Balances[0].Value != 10 || a.UnitCounters[1].Balances[1].Value != 10 { for _, uc := range a.UnitCounters { t.Logf("UC: %+v", uc) for _, b := range uc.Balances { t.Logf("B: %+v", b) } } t.Errorf("Error adding unit counters: %v", len(a.UnitCounters)) } a.InitCounters() if len(a.UnitCounters) != 4 || len(a.UnitCounters[1].Balances) != 2 || a.UnitCounters[1].Balances[0].Value != 10 || a.UnitCounters[1].Balances[1].Value != 10 { for _, uc := range a.UnitCounters { t.Logf("UC: %+v", uc) for _, b := range uc.Balances { t.Logf("B: %+v", b) } } t.Errorf("Error keeping counter values after init: %v", len(a.UnitCounters)) } }
func TestBalanceMatchFilterId(t *testing.T) { mb1 := &Balance{ID: "T1", Weight: 2, precision: 2, RatingSubject: "2", DestinationIDs: utils.NewStringMap("NAT")} mb2 := &BalanceFilter{ID: utils.StringPointer("T1"), Weight: utils.Float64Pointer(1), RatingSubject: utils.StringPointer("1"), DestinationIDs: nil} if !mb1.MatchFilter(mb2, false) { t.Errorf("Match filter failure: %+v == %+v", mb1, mb2) } }
func TestLoadActions(t *testing.T) { if len(csvr.actions) != 9 { t.Error("Failed to load actions: ", len(csvr.actions)) } as1 := csvr.actions["MINI"] expected := []*Action{ &Action{ Id: "MINI0", ActionType: TOPUP_RESET, BalanceType: utils.MONETARY, ExpirationString: UNLIMITED, ExtraParameters: "", Weight: 10, Balance: &Balance{ Uuid: as1[0].Balance.Uuid, Directions: utils.NewStringMap(utils.OUT), Value: 10, Weight: 10, TimingIDs: utils.StringMap{}, SharedGroups: utils.StringMap{}, }, }, &Action{ Id: "MINI1", ActionType: TOPUP, BalanceType: utils.VOICE, ExpirationString: UNLIMITED, ExtraParameters: "", Weight: 10, Balance: &Balance{ Uuid: as1[1].Balance.Uuid, Directions: utils.NewStringMap(utils.OUT), Value: 100, Weight: 10, RatingSubject: "test", DestinationIds: utils.NewStringMap("NAT"), TimingIDs: utils.StringMap{}, SharedGroups: utils.StringMap{}, }, }, } if !reflect.DeepEqual(as1[1], expected[1]) { t.Errorf("Error loading action1: %+v", as1[0].Balance) } as2 := csvr.actions["SHARED"] expected = []*Action{ &Action{ Id: "SHARED0", ActionType: TOPUP, BalanceType: utils.MONETARY, ExpirationString: UNLIMITED, Weight: 10, Balance: &Balance{ Directions: utils.NewStringMap(utils.OUT), DestinationIds: utils.StringMap{}, Uuid: as2[0].Balance.Uuid, Value: 100, Weight: 10, SharedGroups: utils.NewStringMap("SG1"), TimingIDs: utils.StringMap{}, }, }, } if !reflect.DeepEqual(as2[0], expected[0]) { t.Errorf("Error loading action: %+v", as2[0].Balance) } as3 := csvr.actions["DEFEE"] expected = []*Action{ &Action{ Id: "DEFEE0", ActionType: CDRLOG, ExtraParameters: `{"Category":"^ddi","MediationRunId":"^did_run"}`, Weight: 10, Balance: &Balance{ Uuid: as3[0].Balance.Uuid, Directions: utils.StringMap{}, DestinationIds: utils.StringMap{}, TimingIDs: utils.StringMap{}, SharedGroups: utils.StringMap{}, }, }, } if !reflect.DeepEqual(as3, expected) { t.Errorf("Error loading action: %+v", as3[0].Balance) } }
func TestUnitCountersCountAllMonetary(t *testing.T) { a := &Account{ ActionTriggers: ActionTriggers{ &ActionTrigger{ UniqueID: "TestTR1", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, BalanceType: utils.MONETARY, BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), BalanceWeight: 10, }, &ActionTrigger{ UniqueID: "TestTR11", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, BalanceType: utils.MONETARY, BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), BalanceWeight: 10, }, &ActionTrigger{ UniqueID: "TestTR2", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, BalanceType: utils.VOICE, BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), BalanceWeight: 10, }, &ActionTrigger{ UniqueID: "TestTR3", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, BalanceType: utils.VOICE, BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), BalanceWeight: 10, }, &ActionTrigger{ UniqueID: "TestTR4", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, BalanceType: utils.SMS, BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), BalanceWeight: 10, }, &ActionTrigger{ UniqueID: "TestTR5", ThresholdType: utils.TRIGGER_MAX_BALANCE, BalanceType: utils.SMS, BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), BalanceWeight: 10, }, }, } a.InitCounters() a.UnitCounters.addUnits(10, utils.MONETARY, &CallCost{}, nil) if len(a.UnitCounters) != 4 || len(a.UnitCounters[0].Balances) != 2 || a.UnitCounters[0].Balances[0].Value != 10 || a.UnitCounters[0].Balances[1].Value != 10 { for _, uc := range a.UnitCounters { t.Logf("UC: %+v", uc) for _, b := range uc.Balances { t.Logf("B: %+v", b) } } t.Errorf("Error Initializing adding unit counters: %v", len(a.UnitCounters)) } }
func TestUnitsCounterAddBalanceExists(t *testing.T) { uc := &UnitCounter{ BalanceType: utils.SMS, Balances: BalanceChain{&Balance{Value: 1}, &Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}, } UnitCounters{uc}.addUnits(5, utils.SMS, &CallCost{Destination: "0723"}, nil) if len(uc.Balances) != 3 || uc.Balances[1].GetValue() != 15 { t.Error("Error adding minute bucket!") } }
func TestUnitCountersKeepValuesAfterInit(t *testing.T) { a := &Account{ ActionTriggers: ActionTriggers{ &ActionTrigger{ UniqueID: "TestTR1", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(10), }, }, &ActionTrigger{ UniqueID: "TestTR11", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20), }, }, &ActionTrigger{ UniqueID: "TestTR2", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), Weight: utils.Float64Pointer(10), }, }, &ActionTrigger{ UniqueID: "TestTR22", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(10), }, }, &ActionTrigger{ UniqueID: "TestTR3", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(10), }, }, &ActionTrigger{ UniqueID: "TestTR4", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(10), }, }, &ActionTrigger{ UniqueID: "TestTR5", ThresholdType: utils.TRIGGER_MAX_BALANCE, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(10), }, }, }, } a.InitCounters() a.UnitCounters.addUnits(10, utils.VOICE, &CallCost{Destination: "0723045326"}, nil) if len(a.UnitCounters) != 3 || len(a.UnitCounters[utils.VOICE][0].Counters) != 2 || a.UnitCounters[utils.VOICE][0].Counters[0].Value != 10 || a.UnitCounters[utils.VOICE][0].Counters[1].Value != 10 { for key, counters := range a.UnitCounters { t.Log(key) for _, uc := range counters { t.Logf("UC: %+v", uc) for _, b := range uc.Counters { t.Logf("B: %+v", b) } } } t.Errorf("Error adding unit counters: %v", len(a.UnitCounters)) } a.InitCounters() if len(a.UnitCounters) != 3 || len(a.UnitCounters[utils.VOICE][0].Counters) != 2 || a.UnitCounters[utils.VOICE][0].Counters[0].Value != 10 || a.UnitCounters[utils.VOICE][0].Counters[1].Value != 10 { for key, counters := range a.UnitCounters { t.Log(key) for _, uc := range counters { t.Logf("UC: %+v", uc) for _, b := range uc.Counters { t.Logf("B: %+v", b) } } } t.Errorf("Error keeping counter values after init: %v", len(a.UnitCounters)) } }
func populateDB() { ats := []*Action{ &Action{ActionType: "*topup", BalanceType: utils.MONETARY, Balance: &Balance{Value: 10}}, &Action{ActionType: "*topup", BalanceType: utils.VOICE, Balance: &Balance{Weight: 20, Value: 10, DestinationIds: utils.NewStringMap("NAT")}}, } ats1 := []*Action{ &Action{ActionType: "*topup", BalanceType: utils.MONETARY, Balance: &Balance{Value: 10}, Weight: 10}, &Action{ActionType: "*reset_account", Weight: 20}, } minu := &Account{ Id: "vdf:minu", BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{&Balance{Value: 50}}, utils.VOICE: BalanceChain{ &Balance{Value: 200, DestinationIds: utils.NewStringMap("NAT"), Weight: 10}, &Balance{Value: 100, DestinationIds: utils.NewStringMap("RET"), Weight: 20}, }}, } broker := &Account{ Id: "vdf:broker", BalanceMap: map[string]BalanceChain{ utils.VOICE: BalanceChain{ &Balance{Value: 20, DestinationIds: utils.NewStringMap("NAT"), Weight: 10, RatingSubject: "rif"}, &Balance{Value: 100, DestinationIds: utils.NewStringMap("RET"), Weight: 20}, }}, } luna := &Account{ Id: "vdf:luna", BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{ &Balance{Value: 0, Weight: 20}, }}, } // this is added to test if csv load tests account will not overwrite balances minitsboy := &Account{ Id: "vdf:minitsboy", BalanceMap: map[string]BalanceChain{ utils.VOICE: BalanceChain{ &Balance{Value: 20, DestinationIds: utils.NewStringMap("NAT"), Weight: 10, RatingSubject: "rif"}, &Balance{Value: 100, DestinationIds: utils.NewStringMap("RET"), Weight: 20}, }, utils.MONETARY: BalanceChain{ &Balance{Value: 100, Weight: 10}, }, }, } max := &Account{ Id: "cgrates.org:max", BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{ &Balance{Value: 11, Weight: 20}, }}, } money := &Account{ Id: "cgrates.org:money", BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{ &Balance{Value: 10000, Weight: 10}, }}, } if accountingStorage != nil && ratingStorage != nil { ratingStorage.SetActions("TEST_ACTIONS", ats) ratingStorage.SetActions("TEST_ACTIONS_ORDER", ats1) accountingStorage.SetAccount(broker) accountingStorage.SetAccount(minu) accountingStorage.SetAccount(minitsboy) accountingStorage.SetAccount(luna) accountingStorage.SetAccount(max) accountingStorage.SetAccount(money) } else { log.Fatal("Could not connect to db!") } }
func TestBalanceMatchFilterId(t *testing.T) { mb1 := &Balance{Id: "T1", Weight: 2, precision: 2, RatingSubject: "2", DestinationIds: utils.NewStringMap("NAT")} mb2 := &Balance{Id: "T1", Weight: 1, precision: 1, RatingSubject: "1", DestinationIds: utils.StringMap{}} if !mb1.MatchFilter(mb2, false) { t.Errorf("Match filter failure: %+v == %+v", mb1, mb2) } }
func TestLoadActions(t *testing.T) { if len(csvr.actions) != 15 { t.Error("Failed to load actions: ", len(csvr.actions)) } as1 := csvr.actions["MINI"] expected := []*Action{ &Action{ Id: "MINI", ActionType: TOPUP_RESET, ExpirationString: UNLIMITED, ExtraParameters: "", Weight: 10, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Uuid: as1[0].Balance.Uuid, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Value: &utils.ValueFormula{Static: 10}, Weight: utils.Float64Pointer(10), DestinationIDs: nil, TimingIDs: nil, SharedGroups: nil, Categories: nil, Disabled: utils.BoolPointer(false), Blocker: utils.BoolPointer(false), }, }, &Action{ Id: "MINI", ActionType: TOPUP, ExpirationString: UNLIMITED, ExtraParameters: "", Weight: 10, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Uuid: as1[1].Balance.Uuid, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Value: &utils.ValueFormula{Static: 100}, Weight: utils.Float64Pointer(10), RatingSubject: utils.StringPointer("test"), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), TimingIDs: nil, SharedGroups: nil, Categories: nil, Disabled: utils.BoolPointer(false), Blocker: utils.BoolPointer(false), }, }, } if !reflect.DeepEqual(as1, expected) { t.Errorf("Error loading action1: %s", utils.ToIJSON(as1)) } as2 := csvr.actions["SHARED"] expected = []*Action{ &Action{ Id: "SHARED", ActionType: TOPUP, ExpirationString: UNLIMITED, Weight: 10, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), DestinationIDs: nil, Uuid: as2[0].Balance.Uuid, Value: &utils.ValueFormula{Static: 100}, Weight: utils.Float64Pointer(10), SharedGroups: utils.StringMapPointer(utils.NewStringMap("SG1")), TimingIDs: nil, Categories: nil, Disabled: utils.BoolPointer(false), Blocker: utils.BoolPointer(false), }, }, } if !reflect.DeepEqual(as2, expected) { t.Errorf("Error loading action: %s", utils.ToIJSON(as2)) } as3 := csvr.actions["DEFEE"] expected = []*Action{ &Action{ Id: "DEFEE", ActionType: CDRLOG, ExtraParameters: `{"Category":"^ddi","MediationRunId":"^did_run"}`, Weight: 10, Balance: &BalanceFilter{ Uuid: as3[0].Balance.Uuid, Directions: nil, DestinationIDs: nil, TimingIDs: nil, Categories: nil, SharedGroups: nil, Blocker: utils.BoolPointer(false), Disabled: utils.BoolPointer(false), }, }, } if !reflect.DeepEqual(as3, expected) { t.Errorf("Error loading action: %+v", as3[0].Balance) } }
func (ub *Account) debitCreditBalance(cd *CallDescriptor, count bool, dryRun bool, goNegative bool) (cc *CallCost, err error) { usefulUnitBalances := ub.getAlldBalancesForPrefix(cd.Destination, cd.Category, cd.Direction, cd.TOR) usefulMoneyBalances := ub.getAlldBalancesForPrefix(cd.Destination, cd.Category, cd.Direction, utils.MONETARY) //utils.Logger.Info(fmt.Sprintf("%+v, %+v", usefulMoneyBalances, usefulUnitBalances)) //utils.Logger.Info(fmt.Sprintf("STARTCD: %+v", cd)) //log.Printf("%+v, %+v", usefulMoneyBalances, usefulUnitBalances) var leftCC *CallCost cc = cd.CreateCallCost() generalBalanceChecker := true for generalBalanceChecker { generalBalanceChecker = false // debit minutes unitBalanceChecker := true for unitBalanceChecker { // try every balance multiple times in case one becomes active or ratig changes unitBalanceChecker = false //log.Printf("InitialCD: %+v", cd) for _, balance := range usefulUnitBalances { //utils.Logger.Info(fmt.Sprintf("Unit balance: %+v", balance)) //utils.Logger.Info(fmt.Sprintf("CD BEFORE UNIT: %+v", cd)) partCC, debitErr := balance.debitUnits(cd, balance.account, usefulMoneyBalances, count, dryRun, len(cc.Timespans) == 0) if debitErr != nil { return nil, debitErr } //utils.Logger.Info(fmt.Sprintf("CD AFTER UNIT: %+v", cd)) if partCC != nil { //log.Printf("partCC: %+v", partCC.Timespans[0]) cc.Timespans = append(cc.Timespans, partCC.Timespans...) cc.negativeConnectFee = partCC.negativeConnectFee // for i, ts := range cc.Timespans { // log.Printf("cc.times[an[%d]: %+v\n", i, ts) // } cd.TimeStart = cc.GetEndTime() //log.Printf("CD: %+v", cd) //log.Printf("CD: %+v - %+v", cd.TimeStart, cd.TimeEnd) // check if the calldescriptor is covered if cd.GetDuration() <= 0 { goto COMMIT } unitBalanceChecker = true generalBalanceChecker = true // check for max cost disconnect if dryRun && partCC.maxCostDisconect { // only return if we are in dry run (max call duration) return } } // check for blocker if dryRun && balance.Blocker { //log.Print("BLOCKER!") return // don't go to next balances } } } // debit money moneyBalanceChecker := true for moneyBalanceChecker { // try every balance multiple times in case one becomes active or ratig changes moneyBalanceChecker = false for _, balance := range usefulMoneyBalances { //utils.Logger.Info(fmt.Sprintf("Money balance: %+v", balance)) //utils.Logger.Info(fmt.Sprintf("CD BEFORE MONEY: %+v", cd)) partCC, debitErr := balance.debitMoney(cd, balance.account, usefulMoneyBalances, count, dryRun, len(cc.Timespans) == 0) if debitErr != nil { return nil, debitErr } //utils.Logger.Info(fmt.Sprintf("CD AFTER MONEY: %+v", cd)) if partCC != nil { cc.Timespans = append(cc.Timespans, partCC.Timespans...) cc.negativeConnectFee = partCC.negativeConnectFee /*for i, ts := range cc.Timespans { log.Printf("cc.times[an[%d]: %+v\n", i, ts) }*/ cd.TimeStart = cc.GetEndTime() //log.Printf("CD: %+v", cd) //log.Printf("CD: %+v - %+v", cd.TimeStart, cd.TimeEnd) // check if the calldescriptor is covered if cd.GetDuration() <= 0 { goto COMMIT } moneyBalanceChecker = true generalBalanceChecker = true if dryRun && partCC.maxCostDisconect { // only return if we are in dry run (max call duration) return } } // check for blocker if dryRun && balance.Blocker { //log.Print("BLOCKER!") return // don't go to next balances } } } //log.Printf("END CD: %+v", cd) //log.Print("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") } //log.Printf("After balances CD: %+v", cd) leftCC, err = cd.getCost() if err != nil { utils.Logger.Err(fmt.Sprintf("Error getting new cost for balance subject: %v", err)) } if leftCC.Cost == 0 && len(leftCC.Timespans) > 0 { // put AccountID ubformation in increments for _, ts := range leftCC.Timespans { for _, inc := range ts.Increments { if inc.BalanceInfo == nil { inc.BalanceInfo = &DebitInfo{} } inc.BalanceInfo.AccountID = ub.ID } } cc.Timespans = append(cc.Timespans, leftCC.Timespans...) } 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, true) } //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 && !dryRun { utils.Logger.Err(fmt.Sprintf("<Rater> Going negative on account %s with AllowNegative: false", cd.GetAccountKey())) } leftCC.Timespans.Decompress() for _, ts := range leftCC.Timespans { if ts.Increments == nil { ts.createIncrementsSlice() } for _, increment := range ts.Increments { cost := increment.Cost defaultBalance := ub.GetDefaultMoneyBalance() defaultBalance.SubstractValue(cost) increment.BalanceInfo.Monetary = &MonetaryInfo{ UUID: defaultBalance.Uuid, ID: defaultBalance.ID, Value: defaultBalance.Value, } increment.BalanceInfo.AccountID = ub.ID increment.paid = true if count { ub.countUnits( cost, utils.MONETARY, leftCC, &Balance{ Directions: utils.StringMap{leftCC.Direction: true}, Value: cost, DestinationIDs: utils.NewStringMap(leftCC.Destination), }) } } } } COMMIT: if !dryRun { // save darty shared balances usefulMoneyBalances.SaveDirtyBalances(ub) usefulUnitBalances.SaveDirtyBalances(ub) } //log.Printf("Final CC: %+v", cc) return }