mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-24 16:48:45 +05:00
refactorings, started groups
This commit is contained in:
@@ -64,6 +64,22 @@ func (at *ActionTrigger) Execute(ub *UserBalance) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// returns true if the field of the action timing are equeal to the non empty
|
||||
// fields of the action
|
||||
func (at *ActionTrigger) Match(a *Action) bool {
|
||||
if a == nil {
|
||||
return true
|
||||
}
|
||||
id := a.BalanceId == "" || at.BalanceId == a.BalanceId
|
||||
direction := a.Direction == "" || at.Direction == a.Direction
|
||||
thresholdType, thresholdValue := true, true
|
||||
if a.MinuteBucket != nil {
|
||||
thresholdType = a.MinuteBucket.PriceType == "" || at.ThresholdType == a.MinuteBucket.PriceType
|
||||
thresholdValue = a.MinuteBucket.Price == 0 || at.ThresholdValue == a.MinuteBucket.Price
|
||||
}
|
||||
return id && direction && thresholdType && thresholdValue
|
||||
}
|
||||
|
||||
// Structure to store actions according to weight
|
||||
type ActionTriggerPriotityList []*ActionTrigger
|
||||
|
||||
|
||||
@@ -400,6 +400,110 @@ func TestActionTimingPriotityListWeight(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionTriggerMatchNil(t *testing.T) {
|
||||
at := &ActionTrigger{
|
||||
Direction: OUTBOUND,
|
||||
BalanceId: CREDIT,
|
||||
ThresholdType: TRIGGER_MAX_BALANCE,
|
||||
ThresholdValue: 2,
|
||||
}
|
||||
var a *Action
|
||||
if !at.Match(a) {
|
||||
t.Errorf("Action trigger [%v] does not match action [%v]", at, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionTriggerMatchAllBlank(t *testing.T) {
|
||||
at := &ActionTrigger{
|
||||
Direction: OUTBOUND,
|
||||
BalanceId: CREDIT,
|
||||
ThresholdType: TRIGGER_MAX_BALANCE,
|
||||
ThresholdValue: 2,
|
||||
}
|
||||
a := &Action{}
|
||||
if !at.Match(a) {
|
||||
t.Errorf("Action trigger [%v] does not match action [%v]", at, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionTriggerMatchMinuteBucketBlank(t *testing.T) {
|
||||
at := &ActionTrigger{
|
||||
Direction: OUTBOUND,
|
||||
BalanceId: CREDIT,
|
||||
ThresholdType: TRIGGER_MAX_BALANCE,
|
||||
ThresholdValue: 2,
|
||||
}
|
||||
a := &Action{Direction: OUTBOUND, BalanceId: CREDIT}
|
||||
if !at.Match(a) {
|
||||
t.Errorf("Action trigger [%v] does not match action [%v]", at, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionTriggerMatchMinuteBucketFull(t *testing.T) {
|
||||
at := &ActionTrigger{
|
||||
Direction: OUTBOUND,
|
||||
BalanceId: CREDIT,
|
||||
ThresholdType: TRIGGER_MAX_BALANCE,
|
||||
ThresholdValue: 2,
|
||||
}
|
||||
a := &Action{MinuteBucket: &MinuteBucket{PriceType: TRIGGER_MAX_BALANCE, Price: 2}}
|
||||
if !at.Match(a) {
|
||||
t.Errorf("Action trigger [%v] does not match action [%v]", at, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionTriggerMatchAllFull(t *testing.T) {
|
||||
at := &ActionTrigger{
|
||||
Direction: OUTBOUND,
|
||||
BalanceId: CREDIT,
|
||||
ThresholdType: TRIGGER_MAX_BALANCE,
|
||||
ThresholdValue: 2,
|
||||
}
|
||||
a := &Action{Direction: OUTBOUND, BalanceId: CREDIT, MinuteBucket: &MinuteBucket{PriceType: TRIGGER_MAX_BALANCE, Price: 2}}
|
||||
if !at.Match(a) {
|
||||
t.Errorf("Action trigger [%v] does not match action [%v]", at, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionTriggerMatchSomeFalse(t *testing.T) {
|
||||
at := &ActionTrigger{
|
||||
Direction: OUTBOUND,
|
||||
BalanceId: CREDIT,
|
||||
ThresholdType: TRIGGER_MAX_BALANCE,
|
||||
ThresholdValue: 2,
|
||||
}
|
||||
a := &Action{Direction: INBOUND, BalanceId: CREDIT, MinuteBucket: &MinuteBucket{PriceType: TRIGGER_MAX_BALANCE, Price: 2}}
|
||||
if at.Match(a) {
|
||||
t.Errorf("Action trigger [%v] does not match action [%v]", at, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionTriggerMatcMinuteBucketFalse(t *testing.T) {
|
||||
at := &ActionTrigger{
|
||||
Direction: OUTBOUND,
|
||||
BalanceId: CREDIT,
|
||||
ThresholdType: TRIGGER_MAX_BALANCE,
|
||||
ThresholdValue: 2,
|
||||
}
|
||||
a := &Action{Direction: OUTBOUND, BalanceId: CREDIT, MinuteBucket: &MinuteBucket{PriceType: TRIGGER_MAX_BALANCE, Price: 3}}
|
||||
if at.Match(a) {
|
||||
t.Errorf("Action trigger [%v] does not match action [%v]", at, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionTriggerMatcAllFalse(t *testing.T) {
|
||||
at := &ActionTrigger{
|
||||
Direction: OUTBOUND,
|
||||
BalanceId: CREDIT,
|
||||
ThresholdType: TRIGGER_MAX_BALANCE,
|
||||
ThresholdValue: 2,
|
||||
}
|
||||
a := &Action{Direction: INBOUND, BalanceId: MINUTES, MinuteBucket: &MinuteBucket{PriceType: TRIGGER_MAX_COUNTER, Price: 3}}
|
||||
if at.Match(a) {
|
||||
t.Errorf("Action trigger [%v] does not match action [%v]", at, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionTriggerPriotityList(t *testing.T) {
|
||||
at1 := &ActionTrigger{Weight: 10}
|
||||
at2 := &ActionTrigger{Weight: 20}
|
||||
|
||||
119
engine/balances.go
Normal file
119
engine/balances.go
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
Rating system designed to be used in VoIP Carriers World
|
||||
Copyright (C) 2013 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 <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
package engine
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Balance struct {
|
||||
Id string
|
||||
Value float64
|
||||
ExpirationDate time.Time
|
||||
Weight float64
|
||||
GroupIds []string
|
||||
SpecialPercent float64
|
||||
}
|
||||
|
||||
func (b *Balance) Equal(o *Balance) bool {
|
||||
return b.ExpirationDate.Equal(o.ExpirationDate) ||
|
||||
b.Weight == o.Weight
|
||||
}
|
||||
|
||||
func (b *Balance) IsExpired() bool {
|
||||
return !b.ExpirationDate.IsZero() && b.ExpirationDate.Before(time.Now())
|
||||
}
|
||||
|
||||
func (b *Balance) Clone() *Balance {
|
||||
return &Balance{
|
||||
Id: b.Id,
|
||||
Value: b.Value,
|
||||
ExpirationDate: b.ExpirationDate,
|
||||
Weight: b.Weight,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Structure to store minute buckets according to weight, precision or price.
|
||||
*/
|
||||
type BalanceChain []*Balance
|
||||
|
||||
func (bc BalanceChain) Len() int {
|
||||
return len(bc)
|
||||
}
|
||||
|
||||
func (bc BalanceChain) Swap(i, j int) {
|
||||
bc[i], bc[j] = bc[j], bc[i]
|
||||
}
|
||||
|
||||
func (bc BalanceChain) Less(j, i int) bool {
|
||||
return bc[i].Weight < bc[j].Weight
|
||||
}
|
||||
|
||||
func (bc BalanceChain) Sort() {
|
||||
sort.Sort(bc)
|
||||
}
|
||||
|
||||
func (bc BalanceChain) GetTotalValue() (total float64) {
|
||||
for _, b := range bc {
|
||||
if !b.IsExpired() {
|
||||
total += b.Value
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (bc BalanceChain) Debit(amount float64) float64 {
|
||||
bc.Sort()
|
||||
for i, b := range bc {
|
||||
if b.IsExpired() {
|
||||
continue
|
||||
}
|
||||
if b.Value >= amount || i == len(bc)-1 { // if last one go negative
|
||||
b.Value -= amount
|
||||
break
|
||||
}
|
||||
b.Value = 0
|
||||
amount -= b.Value
|
||||
}
|
||||
return bc.GetTotalValue()
|
||||
}
|
||||
|
||||
func (bc BalanceChain) Equal(o BalanceChain) bool {
|
||||
if len(bc) != len(o) {
|
||||
return false
|
||||
}
|
||||
bc.Sort()
|
||||
o.Sort()
|
||||
for i := 0; i < len(bc); i++ {
|
||||
if !bc[i].Equal(o[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (bc BalanceChain) Clone() BalanceChain {
|
||||
var newChain BalanceChain
|
||||
for _, b := range bc {
|
||||
newChain = append(newChain, b.Clone())
|
||||
}
|
||||
return newChain
|
||||
}
|
||||
@@ -369,7 +369,9 @@ func (cd *CallDescriptor) Debit() (cc *CallCost, err error) {
|
||||
}
|
||||
for _, ts := range cc.Timespans {
|
||||
if ts.MinuteInfo != nil {
|
||||
userBalance.debitMinutesBalance(ts.MinuteInfo.Quantity, cd.Destination, true)
|
||||
if err = userBalance.debitMinutesBalance(ts.MinuteInfo.Quantity, cd.Destination, true); err != nil {
|
||||
return cc, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -387,8 +389,7 @@ func (cd *CallDescriptor) MaxDebit(startTime time.Time) (cc *CallCost, err error
|
||||
return new(CallCost), errors.New("no more credit")
|
||||
}
|
||||
if remainingSeconds > 0 { // for postpaying client returns -1
|
||||
rs, _ := time.ParseDuration(fmt.Sprintf("%vs", remainingSeconds))
|
||||
cd.TimeEnd = cd.TimeStart.Add(rs)
|
||||
cd.TimeEnd = cd.TimeStart.Add(time.Duration(remainingSeconds) * time.Second)
|
||||
}
|
||||
return cd.Debit()
|
||||
}
|
||||
|
||||
46
engine/groups.go
Normal file
46
engine/groups.go
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
Rating system designed to be used in VoIP Carriers World
|
||||
Copyright (C) 2013 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 <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
package engine
|
||||
|
||||
import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
type GroupLink struct {
|
||||
Id string
|
||||
Weight float64
|
||||
}
|
||||
|
||||
type GroupLinks []*GroupLink
|
||||
|
||||
func (gls GroupLinks) Len() int {
|
||||
return len(gls)
|
||||
}
|
||||
|
||||
func (gls GroupLinks) Swap(i, j int) {
|
||||
gls[i], gls[j] = gls[j], gls[i]
|
||||
}
|
||||
|
||||
func (gls GroupLinks) Less(j, i int) bool {
|
||||
return gls[i].Weight < gls[j].Weight
|
||||
}
|
||||
|
||||
func (gls GroupLinks) Sort() {
|
||||
sort.Sort(gls)
|
||||
}
|
||||
@@ -21,9 +21,7 @@ package engine
|
||||
import (
|
||||
"errors"
|
||||
"github.com/cgrates/cgrates/utils"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -41,6 +39,11 @@ const (
|
||||
// action price type
|
||||
PRICE_PERCENT = "*percent"
|
||||
PRICE_ABSOLUTE = "*absolute"
|
||||
// action trigger threshold types
|
||||
TRIGGER_MIN_COUNTER = "*min_counter"
|
||||
TRIGGER_MAX_COUNTER = "*max_counter"
|
||||
TRIGGER_MIN_BALANCE = "*min_balance"
|
||||
TRIGGER_MAX_BALANCE = "*max_balance"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -49,6 +52,7 @@ var (
|
||||
|
||||
/*
|
||||
Structure containing information about user's credit (minutes, cents, sms...).'
|
||||
This can represent a user or a shared group.
|
||||
*/
|
||||
type UserBalance struct {
|
||||
Id string
|
||||
@@ -57,104 +61,10 @@ type UserBalance struct {
|
||||
MinuteBuckets []*MinuteBucket
|
||||
UnitCounters []*UnitsCounter
|
||||
ActionTriggers ActionTriggerPriotityList
|
||||
|
||||
Groups GroupLinks // user info about groups
|
||||
// group information
|
||||
GroupIds []string
|
||||
UserIds []string
|
||||
}
|
||||
|
||||
type Balance struct {
|
||||
Id string
|
||||
Value float64
|
||||
ExpirationDate time.Time
|
||||
Weight float64
|
||||
GroupIds []string
|
||||
Percent float64
|
||||
}
|
||||
|
||||
func (b *Balance) Equal(o *Balance) bool {
|
||||
return b.ExpirationDate.Equal(o.ExpirationDate) ||
|
||||
b.Weight == o.Weight
|
||||
}
|
||||
|
||||
func (b *Balance) IsExpired() bool {
|
||||
return !b.ExpirationDate.IsZero() && b.ExpirationDate.Before(time.Now())
|
||||
}
|
||||
|
||||
func (b *Balance) Clone() *Balance {
|
||||
return &Balance{
|
||||
Id: b.Id,
|
||||
Value: b.Value,
|
||||
ExpirationDate: b.ExpirationDate,
|
||||
Weight: b.Weight,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Structure to store minute buckets according to weight, precision or price.
|
||||
*/
|
||||
type BalanceChain []*Balance
|
||||
|
||||
func (bc BalanceChain) Len() int {
|
||||
return len(bc)
|
||||
}
|
||||
|
||||
func (bc BalanceChain) Swap(i, j int) {
|
||||
bc[i], bc[j] = bc[j], bc[i]
|
||||
}
|
||||
|
||||
func (bc BalanceChain) Less(j, i int) bool {
|
||||
return bc[i].Weight < bc[j].Weight
|
||||
}
|
||||
|
||||
func (bc BalanceChain) Sort() {
|
||||
sort.Sort(bc)
|
||||
}
|
||||
|
||||
func (bc BalanceChain) GetTotalValue() (total float64) {
|
||||
for _, b := range bc {
|
||||
if !b.IsExpired() {
|
||||
total += b.Value
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (bc BalanceChain) Debit(amount float64) float64 {
|
||||
bc.Sort()
|
||||
for i, b := range bc {
|
||||
if b.IsExpired() {
|
||||
continue
|
||||
}
|
||||
if b.Value >= amount || i == len(bc)-1 { // if last one go negative
|
||||
b.Value -= amount
|
||||
break
|
||||
}
|
||||
b.Value = 0
|
||||
amount -= b.Value
|
||||
}
|
||||
return bc.GetTotalValue()
|
||||
}
|
||||
|
||||
func (bc BalanceChain) Equal(o BalanceChain) bool {
|
||||
if len(bc) != len(o) {
|
||||
return false
|
||||
}
|
||||
bc.Sort()
|
||||
o.Sort()
|
||||
for i := 0; i < len(bc); i++ {
|
||||
if !bc[i].Equal(o[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (bc BalanceChain) Clone() BalanceChain {
|
||||
var newChain BalanceChain
|
||||
for _, b := range bc {
|
||||
newChain = append(newChain, b.Clone())
|
||||
}
|
||||
return newChain
|
||||
UserIds []string // group info about users
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -309,11 +219,7 @@ func (ub *UserBalance) executeActionTriggers(a *Action) {
|
||||
// the next reset (see RESET_TRIGGERS action type)
|
||||
continue
|
||||
}
|
||||
if a != nil && (at.BalanceId != a.BalanceId ||
|
||||
at.Direction != a.Direction ||
|
||||
(a.MinuteBucket != nil &&
|
||||
(at.ThresholdType != a.MinuteBucket.PriceType ||
|
||||
at.ThresholdValue != a.MinuteBucket.Price))) {
|
||||
if !at.Match(a) {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(at.ThresholdType, "counter") {
|
||||
@@ -386,11 +292,7 @@ func (ub *UserBalance) executeActionTriggers(a *Action) {
|
||||
// If the action is not nil it acts like a filter
|
||||
func (ub *UserBalance) resetActionTriggers(a *Action) {
|
||||
for _, at := range ub.ActionTriggers {
|
||||
if a != nil && (at.BalanceId != a.BalanceId ||
|
||||
at.Direction != a.Direction ||
|
||||
(a.MinuteBucket != nil &&
|
||||
(at.ThresholdType != a.MinuteBucket.PriceType ||
|
||||
at.ThresholdValue != a.MinuteBucket.Price))) {
|
||||
if !at.Match(a) {
|
||||
continue
|
||||
}
|
||||
at.Executed = false
|
||||
|
||||
Reference in New Issue
Block a user