Added FieldAsInterface for EventCost

This commit is contained in:
Trial97
2020-02-05 11:02:55 +02:00
committed by Dan Christian Bogos
parent 2f8b7dc53e
commit ff5f5f2d21
7 changed files with 532 additions and 121 deletions

View File

@@ -31,10 +31,8 @@ import (
"github.com/cgrates/cgrates/utils"
)
/*
Structure containing information about user's credit (minutes, cents, sms...).'
This can represent a user or a shared group.
*/
// Account structure containing information about user's credit (minutes, cents, sms...).'
// This can represent a user or a shared group.
type Account struct {
ID string
BalanceMap map[string]Balances
@@ -47,17 +45,17 @@ type Account struct {
}
// User's available minutes for the specified destination
func (ub *Account) getCreditForPrefix(cd *CallDescriptor) (duration time.Duration, credit float64, balances Balances) {
creditBalances := ub.getBalancesForPrefix(cd.Destination, cd.Category, utils.MONETARY, "", cd.TimeStart)
func (acc *Account) getCreditForPrefix(cd *CallDescriptor) (duration time.Duration, credit float64, balances Balances) {
creditBalances := acc.getBalancesForPrefix(cd.Destination, cd.Category, utils.MONETARY, "", cd.TimeStart)
unitBalances := ub.getBalancesForPrefix(cd.Destination, cd.Category, cd.ToR, "", cd.TimeStart)
unitBalances := acc.getBalancesForPrefix(cd.Destination, cd.Category, cd.ToR, "", cd.TimeStart)
// gather all balances from shared groups
var extendedCreditBalances Balances
for _, cb := range creditBalances {
if len(cb.SharedGroups) > 0 {
for sg := range cb.SharedGroups {
if sharedGroup, _ := dm.GetSharedGroup(sg, false, utils.NonTransactional); sharedGroup != nil {
sgb := sharedGroup.GetBalances(cd.Destination, cd.Category, utils.MONETARY, ub, cd.TimeStart)
sgb := sharedGroup.GetBalances(cd.Destination, cd.Category, utils.MONETARY, acc, cd.TimeStart)
sgb = sharedGroup.SortBalancesByStrategy(cb, sgb)
extendedCreditBalances = append(extendedCreditBalances, sgb...)
}
@@ -71,7 +69,7 @@ func (ub *Account) getCreditForPrefix(cd *CallDescriptor) (duration time.Duratio
if len(mb.SharedGroups) > 0 {
for sg := range mb.SharedGroups {
if sharedGroup, _ := dm.GetSharedGroup(sg, false, utils.NonTransactional); sharedGroup != nil {
sgb := sharedGroup.GetBalances(cd.Destination, cd.Category, cd.ToR, ub, cd.TimeStart)
sgb := sharedGroup.GetBalances(cd.Destination, cd.Category, cd.ToR, acc, cd.TimeStart)
sgb = sharedGroup.SortBalancesByStrategy(mb, sgb)
extendedMinuteBalances = append(extendedMinuteBalances, sgb...)
}
@@ -186,7 +184,7 @@ func (acc *Account) setBalanceAction(a *Action) error {
// Debits some amount of user's specified balance adding the balance if it does not exists.
// Returns the remaining credit in user's balance.
func (ub *Account) debitBalanceAction(a *Action, reset, resetIfNegative bool) error {
func (acc *Account) debitBalanceAction(a *Action, reset, resetIfNegative bool) error {
if a == nil {
return errors.New("nil action")
}
@@ -195,16 +193,16 @@ func (ub *Account) debitBalanceAction(a *Action, reset, resetIfNegative bool) er
if bClone == nil {
return errors.New("nil balance in action")
}
if ub.BalanceMap == nil {
ub.BalanceMap = make(map[string]Balances)
if acc.BalanceMap == nil {
acc.BalanceMap = make(map[string]Balances)
}
found := false
balanceType := a.Balance.GetType()
for _, b := range ub.BalanceMap[balanceType] {
for _, b := range acc.BalanceMap[balanceType] {
if b.IsExpiredAt(time.Now()) {
continue // just to be safe (cleaned expired balances above)
}
b.account = ub
b.account = acc
if b.MatchFilter(a.Balance, false, false) {
if reset || (resetIfNegative && b.Value < 0) {
b.SetValue(0)
@@ -242,23 +240,23 @@ func (ub *Account) debitBalanceAction(a *Action, reset, resetIfNegative bool) er
utils.Logger.Warning(fmt.Sprintf("Could load value factor from actions: extra parametrs: %s", a.ExtraParameters))
}
}
ub.BalanceMap[balanceType] = append(ub.BalanceMap[balanceType], bClone)
acc.BalanceMap[balanceType] = append(acc.BalanceMap[balanceType], bClone)
_, err := guardian.Guardian.Guard(func() (interface{}, error) {
sgs := make([]string, len(bClone.SharedGroups))
i := 0
for sgId := range bClone.SharedGroups {
for sgID := range bClone.SharedGroups {
// add shared group member
sg, err := dm.GetSharedGroup(sgId, false, utils.NonTransactional)
sg, err := dm.GetSharedGroup(sgID, false, utils.NonTransactional)
if err != nil || sg == nil {
//than is problem
utils.Logger.Warning(fmt.Sprintf("Could not get shared group: %v", sgId))
utils.Logger.Warning(fmt.Sprintf("Could not get shared group: %v", sgID))
} else {
if _, found := sg.MemberIds[ub.ID]; !found {
if _, found := sg.MemberIds[acc.ID]; !found {
// add member and save
if sg.MemberIds == nil {
sg.MemberIds = make(utils.StringMap)
}
sg.MemberIds[ub.ID] = true
sg.MemberIds[acc.ID] = true
dm.SetSharedGroup(sg, utils.NonTransactional)
}
}
@@ -271,17 +269,17 @@ func (ub *Account) debitBalanceAction(a *Action, reset, resetIfNegative bool) er
return err
}
}
ub.InitCounters()
ub.ExecuteActionTriggers(nil)
acc.InitCounters()
acc.ExecuteActionTriggers(nil)
return nil
}
func (ub *Account) getBalancesForPrefix(prefix, category, tor,
func (acc *Account) getBalancesForPrefix(prefix, category, tor,
sharedGroup string, aTime time.Time) Balances {
var balances Balances
balances = append(balances, ub.BalanceMap[tor]...)
balances = append(balances, acc.BalanceMap[tor]...)
if tor != utils.MONETARY && tor != utils.GENERIC {
balances = append(balances, ub.BalanceMap[utils.GENERIC]...)
balances = append(balances, acc.BalanceMap[utils.GENERIC]...)
}
var usefulBalances Balances
@@ -298,15 +296,15 @@ func (ub *Account) getBalancesForPrefix(prefix, category, tor,
if !b.MatchCategory(category) {
continue
}
b.account = ub
b.account = acc
if len(b.DestinationIDs) > 0 && b.DestinationIDs[utils.ANY] == false {
for _, p := range utils.SplitPrefix(prefix, MIN_PREFIX_MATCH) {
if destIDs, err := dm.GetReverseDestination(p, false, utils.NonTransactional); err == nil {
foundResult := false
allInclude := true // whether it is excluded or included
for _, dId := range destIDs {
inclDest, found := b.DestinationIDs[dId]
for _, dID := range destIDs {
inclDest, found := b.DestinationIDs[dID]
if found {
foundResult = true
allInclude = allInclude && inclDest
@@ -348,18 +346,18 @@ func (ub *Account) getBalancesForPrefix(prefix, category, tor,
}
// like getBalancesForPrefix but expanding shared balances
func (account *Account) getAlldBalancesForPrefix(destination, category,
func (acc *Account) getAlldBalancesForPrefix(destination, category,
balanceType string, aTime time.Time) (bc Balances) {
balances := account.getBalancesForPrefix(destination, category, balanceType, "", aTime)
balances := acc.getBalancesForPrefix(destination, category, balanceType, "", aTime)
for _, b := range balances {
if len(b.SharedGroups) > 0 {
for sgId := range b.SharedGroups {
sharedGroup, err := dm.GetSharedGroup(sgId, false, utils.NonTransactional)
for sgID := range b.SharedGroups {
sharedGroup, err := dm.GetSharedGroup(sgID, false, utils.NonTransactional)
if err != nil || sharedGroup == nil {
utils.Logger.Warning(fmt.Sprintf("Could not get shared group: %v", sgId))
utils.Logger.Warning(fmt.Sprintf("Could not get shared group: %v", sgID))
continue
}
sharedBalances := sharedGroup.GetBalances(destination, category, balanceType, account, aTime)
sharedBalances := sharedGroup.GetBalances(destination, category, balanceType, acc, aTime)
sharedBalances = sharedGroup.SortBalancesByStrategy(b, sharedBalances)
bc = append(bc, sharedBalances...)
}
@@ -370,9 +368,9 @@ func (account *Account) getAlldBalancesForPrefix(destination, category,
return
}
func (ub *Account) debitCreditBalance(cd *CallDescriptor, count bool, dryRun bool, goNegative bool) (cc *CallCost, err error) {
usefulUnitBalances := ub.getAlldBalancesForPrefix(cd.Destination, cd.Category, cd.ToR, cd.TimeStart)
usefulMoneyBalances := ub.getAlldBalancesForPrefix(cd.Destination, cd.Category, utils.MONETARY, cd.TimeStart)
func (acc *Account) debitCreditBalance(cd *CallDescriptor, count bool, dryRun bool, goNegative bool) (cc *CallCost, err error) {
usefulUnitBalances := acc.getAlldBalancesForPrefix(cd.Destination, cd.Category, cd.ToR, cd.TimeStart)
usefulMoneyBalances := acc.getAlldBalancesForPrefix(cd.Destination, cd.Category, utils.MONETARY, cd.TimeStart)
var leftCC *CallCost
cc = cd.CreateCallCost()
var hadBalanceSubj bool
@@ -478,7 +476,7 @@ func (ub *Account) debitCreditBalance(cd *CallDescriptor, count bool, dryRun boo
if inc.BalanceInfo == nil {
inc.BalanceInfo = &DebitInfo{}
}
inc.BalanceInfo.AccountID = ub.ID
inc.BalanceInfo.AccountID = acc.ID
}
}
cc.Timespans = append(cc.Timespans, leftCC.Timespans...)
@@ -493,12 +491,12 @@ func (ub *Account) debitCreditBalance(cd *CallDescriptor, count bool, dryRun boo
if initialLength == 0 {
// this is the first add, debit the connect fee
ok, debitedConnectFeeBalance = ub.DebitConnectionFee(cc, usefulMoneyBalances, count, true)
ok, debitedConnectFeeBalance = acc.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 {
if len(leftCC.Timespans) > 0 && leftCC.Cost > 0 && !acc.AllowNegative && !dryRun {
utils.Logger.Warning(fmt.Sprintf("<Rater> Going negative on account %s with AllowNegative: false", cd.GetAccountKey()))
}
leftCC.Timespans.Decompress()
@@ -518,7 +516,7 @@ func (ub *Account) debitCreditBalance(cd *CallDescriptor, count bool, dryRun boo
ID: debitedConnectFeeBalance.ID,
Value: debitedConnectFeeBalance.Value,
},
AccountID: ub.ID,
AccountID: acc.ID,
},
}
@@ -534,11 +532,11 @@ func (ub *Account) debitCreditBalance(cd *CallDescriptor, count bool, dryRun boo
}
cost := increment.Cost
defaultBalance := ub.GetDefaultMoneyBalance()
defaultBalance := acc.GetDefaultMoneyBalance()
defaultBalance.SubstractValue(cost)
//send default balance to thresholdS to be processed
if len(config.CgrConfig().RalsCfg().ThresholdSConns) != 0 {
acntTnt := utils.NewTenantID(ub.ID)
acntTnt := utils.NewTenantID(acc.ID)
thEv := &ArgsProcessEvent{
CGREvent: &utils.CGREvent{
Tenant: acntTnt.Tenant,
@@ -564,10 +562,10 @@ func (ub *Account) debitCreditBalance(cd *CallDescriptor, count bool, dryRun boo
ID: defaultBalance.ID,
Value: defaultBalance.Value,
}
increment.BalanceInfo.AccountID = ub.ID
increment.BalanceInfo.AccountID = acc.ID
increment.paid = true
if count {
ub.countUnits(
acc.countUnits(
cost,
utils.MONETARY,
leftCC,
@@ -583,15 +581,16 @@ func (ub *Account) debitCreditBalance(cd *CallDescriptor, count bool, dryRun boo
COMMIT:
if !dryRun {
// save darty shared balances
usefulMoneyBalances.SaveDirtyBalances(ub)
usefulUnitBalances.SaveDirtyBalances(ub)
usefulMoneyBalances.SaveDirtyBalances(acc)
usefulUnitBalances.SaveDirtyBalances(acc)
}
//log.Printf("Final CC: %+v", cc)
return
}
func (ub *Account) GetDefaultMoneyBalance() *Balance {
for _, balance := range ub.BalanceMap[utils.MONETARY] {
// GetDefaultMoneyBalance returns the defaultmoney balance
func (acc *Account) GetDefaultMoneyBalance() *Balance {
for _, balance := range acc.BalanceMap[utils.MONETARY] {
if balance.IsDefault() {
return balance
}
@@ -601,14 +600,14 @@ func (ub *Account) GetDefaultMoneyBalance() *Balance {
Uuid: utils.GenUUID(),
ID: utils.MetaDefault,
} // minimum weight
if ub.BalanceMap == nil {
ub.BalanceMap = make(map[string]Balances)
if acc.BalanceMap == nil {
acc.BalanceMap = make(map[string]Balances)
}
ub.BalanceMap[utils.MONETARY] = append(ub.BalanceMap[utils.MONETARY], defaultBalance)
acc.BalanceMap[utils.MONETARY] = append(acc.BalanceMap[utils.MONETARY], defaultBalance)
return defaultBalance
}
// Scans the action triggers and execute the actions for which trigger is met
// ExecuteActionTriggers scans the action triggers and execute the actions for which trigger is met
func (acc *Account) ExecuteActionTriggers(a *Action) {
if acc.executingTriggers {
return
@@ -685,7 +684,7 @@ func (acc *Account) ExecuteActionTriggers(a *Action) {
acc.CleanExpiredStuff()
}
// Mark all action trigers as ready for execution
// ResetActionTriggers marks all action trigers as ready for execution
// If the action is not nil it acts like a filter
func (acc *Account) ResetActionTriggers(a *Action) {
for _, at := range acc.ActionTriggers {
@@ -697,7 +696,7 @@ func (acc *Account) ResetActionTriggers(a *Action) {
acc.ExecuteActionTriggers(a)
}
// Sets/Unsets recurrent flag for action triggers
// SetRecurrent sets/unsets recurrent flag for action triggers
func (acc *Account) SetRecurrent(a *Action, recurrent bool) {
for _, at := range acc.ActionTriggers {
if !at.Match(a) {
@@ -713,7 +712,7 @@ func (acc *Account) countUnits(amount float64, kind string, cc *CallCost, b *Bal
acc.ExecuteActionTriggers(nil)
}
// Create counters for all triggered actions
// InitCounters creates counters for all triggered actions
func (acc *Account) InitCounters() {
oldUcs := acc.UnitCounters
acc.UnitCounters = make(UnitCounters)
@@ -766,6 +765,7 @@ func (acc *Account) InitCounters() {
}
}
// CleanExpiredStuff removed expired balances and actiontriggers
func (acc *Account) CleanExpiredStuff() {
if config.CgrConfig().RalsCfg().RemoveExpired {
for key, bm := range acc.BalanceMap {
@@ -797,7 +797,7 @@ func (acc *Account) allBalancesExpired() bool {
return true
}
// returns the shared groups that this user balance belnongs to
// GetSharedGroups returns the shared groups that this user balance belnongs to
func (acc *Account) GetSharedGroups() (groups []string) {
for _, balanceChain := range acc.BalanceMap {
for _, b := range balanceChain {
@@ -809,10 +809,11 @@ func (acc *Account) GetSharedGroups() (groups []string) {
return
}
func (account *Account) GetUniqueSharedGroupMembers(cd *CallDescriptor) (utils.StringMap, error) { // ToDo: make sure we return accountIDs
// GetUniqueSharedGroupMembers returns the acounts from the group
func (acc *Account) GetUniqueSharedGroupMembers(cd *CallDescriptor) (utils.StringMap, error) { // ToDo: make sure we return accountIDs
var balances []*Balance
balances = append(balances, account.getBalancesForPrefix(cd.Destination, cd.Category, utils.MONETARY, "", cd.TimeStart)...)
balances = append(balances, account.getBalancesForPrefix(cd.Destination, cd.Category, cd.ToR, "", cd.TimeStart)...)
balances = append(balances, acc.getBalancesForPrefix(cd.Destination, cd.Category, utils.MONETARY, "", cd.TimeStart)...)
balances = append(balances, acc.getBalancesForPrefix(cd.Destination, cd.Category, cd.ToR, "", cd.TimeStart)...)
// gather all shared group ids
var sharedGroupIds []string
for _, b := range balances {
@@ -834,6 +835,7 @@ func (account *Account) GetUniqueSharedGroupMembers(cd *CallDescriptor) (utils.S
return memberIds, nil
}
// Clone creates a copy of the account
func (acc *Account) Clone() *Account {
newAcc := &Account{
ID: acc.ID,
@@ -856,6 +858,7 @@ func (acc *Account) Clone() *Account {
return newAcc
}
// DebitConnectionFee debits the connection fee
func (acc *Account) DebitConnectionFee(cc *CallCost, usefulMoneyBalances Balances, count bool, block bool) (bool, Balance) {
var debitedBalance Balance
@@ -919,6 +922,7 @@ func (acc *Account) matchActionFilter(condition string) (bool, error) {
return false, nil
}
// GetID returns the account ID
func (acc *Account) GetID() string {
split := strings.Split(acc.ID, utils.CONCATENATED_KEY_SEP)
if len(split) != 2 {
@@ -927,7 +931,7 @@ func (acc *Account) GetID() string {
return split[1]
}
// used in some api for transition
// AsOldStructure used in some api for transition
func (acc *Account) AsOldStructure() interface{} {
type Balance struct {
Uuid string //system wide unique
@@ -1069,6 +1073,7 @@ func (acc *Account) AsOldStructure() interface{} {
return result
}
// AsAccountSummary converts the account into AccountSummary
func (acc *Account) AsAccountSummary() *AccountSummary {
idSplt := strings.Split(acc.ID, utils.CONCATENATED_KEY_SEP)
ad := &AccountSummary{AllowNegative: acc.AllowNegative, Disabled: acc.Disabled}
@@ -1091,8 +1096,9 @@ func (acc *Account) AsAccountSummary() *AccountSummary {
return ad
}
func (acnt *Account) Publish() {
acntTnt := utils.NewTenantID(acnt.ID)
// Publish sends the account to stats and threshold
func (acc *Account) Publish() {
acntTnt := utils.NewTenantID(acc.ID)
cgrEv := &utils.CGREvent{
Tenant: acntTnt.Tenant,
ID: utils.GenUUID(),
@@ -1100,8 +1106,8 @@ func (acnt *Account) Publish() {
utils.EventType: utils.AccountUpdate,
utils.EventSource: utils.AccountService,
utils.Account: acntTnt.ID,
utils.AllowNegative: acnt.AllowNegative,
utils.Disabled: acnt.Disabled}}
utils.AllowNegative: acc.AllowNegative,
utils.Disabled: acc.Disabled}}
if len(config.CgrConfig().RalsCfg().StatSConns) != 0 {
go func() {
var reply []string
@@ -1128,17 +1134,18 @@ func (acnt *Account) Publish() {
}
}
func (acnt *Account) AsNavigableMap(_ []*config.FCTemplate) (*config.NavigableMap, error) {
// AsNavigableMap converts the Account to NavigableMap
func (acc *Account) AsNavigableMap(_ []*config.FCTemplate) (*config.NavigableMap, error) {
mpIface := map[string]interface{}{
"ID": acnt.ID,
"ID": acc.ID,
//"UnitCounters": acnt.UnitCounters,
"ActionTriggers": acnt.ActionTriggers,
"AllowNegative": acnt.AllowNegative,
"Disabled": acnt.Disabled,
"ActionTriggers": acc.ActionTriggers,
"AllowNegative": acc.AllowNegative,
"Disabled": acc.Disabled,
}
balanceMap := make(map[string]interface{}, len(acnt.BalanceMap))
for key, balances := range acnt.BalanceMap {
balanceMap := make(map[string]interface{}, len(acc.BalanceMap))
for key, balances := range acc.BalanceMap {
balSls := make([]*config.NavigableMap, len(balances))
for i, balance := range balances {
balSls[i], _ = balance.AsNavigableMap(nil)
@@ -1151,6 +1158,7 @@ func (acnt *Account) AsNavigableMap(_ []*config.FCTemplate) (*config.NavigableMa
}
// NewAccountSummaryFromJSON creates a new AcccountSummary from a json string
func NewAccountSummaryFromJSON(jsn string) (acntSummary *AccountSummary, err error) {
if !utils.SliceHasMember([]string{"", "null"}, jsn) { // Unmarshal only when content
json.Unmarshal([]byte(jsn), &acntSummary)
@@ -1167,6 +1175,7 @@ type AccountSummary struct {
Disabled bool
}
// Clone creates a copy of the structure
func (as *AccountSummary) Clone() (cln *AccountSummary) {
cln = new(AccountSummary)
cln.Tenant = as.Tenant
@@ -1184,11 +1193,58 @@ func (as *AccountSummary) Clone() (cln *AccountSummary) {
}
// GetBalanceWithID returns a Balance given balance type and balance ID
func (acnt *Account) GetBalanceWithID(blcType, blcID string) (blc *Balance) {
for _, blc = range acnt.BalanceMap[blcType] {
func (acc *Account) GetBalanceWithID(blcType, blcID string) (blc *Balance) {
for _, blc = range acc.BalanceMap[blcType] {
if blc.ID == blcID {
return
}
}
return nil
}
// FieldAsInterface func to help EventCost FieldAsInterface
func (as *AccountSummary) FieldAsInterface(fldPath []string) (val interface{}, err error) {
if len(fldPath) == 0 {
return nil, utils.ErrNotFound
}
switch fldPath[0] {
default:
opath, indx := utils.GetPathIndex(fldPath[0])
if opath == utils.BalanceSummaries && indx != nil {
if len(as.BalanceSummaries) < *indx {
return nil, utils.ErrNotFound
}
bl := as.BalanceSummaries[*indx]
if len(fldPath) == 1 {
return bl, nil
}
return bl.FieldAsInterface(fldPath[1:])
}
return nil, fmt.Errorf("unsupported field prefix: <%s>", fldPath[0])
case utils.Tenant:
if len(fldPath) != 1 {
return nil, utils.ErrNotFound
}
return as.Tenant, nil
case utils.ID:
if len(fldPath) != 1 {
return nil, utils.ErrNotFound
}
return as.ID, nil
case utils.BalanceSummaries:
if len(fldPath) != 1 {
return nil, utils.ErrNotFound
}
return as.BalanceSummaries, nil
case utils.AllowNegative:
if len(fldPath) != 1 {
return nil, utils.ErrNotFound
}
return as.AllowNegative, nil
case utils.Disabled:
if len(fldPath) != 1 {
return nil, utils.ErrNotFound
}
return as.Disabled, nil
}
}

View File

@@ -892,7 +892,7 @@ func (f ValueFactor) GetValue(tor string) float64 {
return 1.0
}
// BalanceDigest represents compressed information about a balance
// BalanceSummary represents compressed information about a balance
type BalanceSummary struct {
UUID string // Balance UUID
ID string // Balance ID if not defined
@@ -901,9 +901,10 @@ type BalanceSummary struct {
Disabled bool
}
// BalanceSummaries is a list of BalanceSummaries
type BalanceSummaries []*BalanceSummary
// GetBalanceSummary returns a BalanceSummary based on an UUID
// BalanceSummaryWithUUD returns a BalanceSummary based on an UUID
func (bs BalanceSummaries) BalanceSummaryWithUUD(bsUUID string) (b *BalanceSummary) {
for _, blc := range bs {
if blc.UUID == bsUUID {
@@ -913,3 +914,24 @@ func (bs BalanceSummaries) BalanceSummaryWithUUD(bsUUID string) (b *BalanceSumma
}
return
}
// FieldAsInterface func to help EventCost FieldAsInterface
func (bl *BalanceSummary) FieldAsInterface(fldPath []string) (val interface{}, err error) {
if len(fldPath) != 1 {
return nil, utils.ErrNotFound
}
switch fldPath[0] {
default:
return nil, fmt.Errorf("unsupported field prefix: <%s>", fldPath[0])
case utils.UUID:
return bl.UUID, nil
case utils.ID:
return bl.ID, nil
case utils.Type:
return bl.Type, nil
case utils.Value:
return bl.Value, nil
case utils.Disabled:
return bl.Disabled, nil
}
}

View File

@@ -21,10 +21,10 @@ package engine
import (
"errors"
"fmt"
"strconv"
"strings"
"net"
"time"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/utils"
)
@@ -840,48 +840,28 @@ func (ec *EventCost) Trim(atUsage time.Duration) (srplusEC *EventCost, err error
return
}
// getIndex returns the path and index if index present
// path[index]=>path,index
// path=>path,nil
func getIndex(spath string) (opath string, idx *int) {
idxStart := strings.Index(spath, utils.IdxStart)
if idxStart == -1 || !strings.HasSuffix(spath, utils.IdxEnd) {
return spath, nil
}
slctr := spath[idxStart+1 : len(spath)-1]
opath = spath[:idxStart]
if strings.HasPrefix(slctr, utils.DynamicDataPrefix) {
return
}
idxVal, err := strconv.Atoi(slctr)
if err != nil {
return spath, nil
}
return opath, &idxVal
}
// FieldAsInterface func to implement DataProvider
func (ec *EventCost) FieldAsInterface(fldPath []string) (val interface{}, err error) {
if len(fldPath) == 0 {
return nil, utils.ErrNotFound
}
switch fldPath[0] {
default: //"Charges [1]"
opath, indx := getIndex(fldPath[0])
default: // "Charges[1]"
opath, indx := utils.GetPathIndex(fldPath[0])
if opath != utils.Charges {
return nil, fmt.Errorf("unsupported field prefix: <%s>", opath)
}
if indx != nil {
chr := ec.Charges[*indx]
if len(fldPath) == 1 {
return chr, nil
}
if fldPath[1] == utils.Rating {
return ec.getRatingForPath(fldPath[2:], ec.Rating[chr.RatingID])
if len(ec.Charges) < *indx {
return nil, utils.ErrNotFound
}
return ec.getChargesForPath(fldPath[1:], ec.Charges[*indx])
}
case utils.Charges: // not needed?
// return ec.Charges.FieldAsInterface(fldPath[1:])
case utils.Charges:
if len(fldPath) != 1 { // slice has no members
return nil, utils.ErrNotFound
}
return ec.Charges, nil
case utils.CGRID:
if len(fldPath) != 1 {
return nil, utils.ErrNotFound
@@ -908,21 +888,57 @@ func (ec *EventCost) FieldAsInterface(fldPath []string) (val interface{}, err er
}
return ec.Cost, nil
case utils.AccountSummary:
// return ec.AccountSummary.FieldAsInterface(fldPath[1:])
case utils.Timings: // not needed?
// return ec.Timings.FieldAsInterface(fldPath[1:])
case utils.Rates: // not needed?
// return ec.Rates.FieldAsInterface(fldPath[1:])
case utils.RatingFilters: // not needed?
// return ec.RatingFilters.FieldAsInterface(fldPath[1:])
case utils.Accounting: // not needed?
// return ec.Accounting.FieldAsInterface(fldPath[1:])
case utils.Rating: // not needed?
// return ec.Rating.FieldAsInterface(fldPath[1:])
return ec.AccountSummary.FieldAsInterface(fldPath[1:])
case utils.Timings:
return ec.Timings.FieldAsInterface(fldPath[1:])
case utils.Rates:
return ec.Rates.FieldAsInterface(fldPath[1:])
case utils.RatingFilters:
return ec.RatingFilters.FieldAsInterface(fldPath[1:])
case utils.Accounting:
return ec.Accounting.FieldAsInterface(fldPath[1:])
case utils.Rating:
return ec.Rating.FieldAsInterface(fldPath[1:])
}
return nil, fmt.Errorf("unsupported field prefix: <%s>", fldPath[0])
}
func (ec *EventCost) getChargesForPath(fldPath []string, chr *ChargingInterval) (val interface{}, err error) {
if chr == nil {
return nil, utils.ErrNotFound
}
if len(fldPath) == 0 {
return chr, nil
}
if fldPath[0] == utils.CompressFactor {
if len(fldPath) != 1 {
return nil, utils.ErrNotFound
}
return chr.CompressFactor, nil
}
if fldPath[0] == utils.Rating {
return ec.getRatingForPath(fldPath[1:], ec.Rating[chr.RatingID])
}
opath, indx := utils.GetPathIndex(fldPath[0])
if opath != utils.Increments {
return nil, fmt.Errorf("unsupported field prefix: <%s>", opath)
}
if indx == nil {
if len(fldPath) != 1 {
return nil, utils.ErrNotFound
}
return chr.Increments, nil
}
incr := chr.Increments[*indx]
if len(fldPath) == 1 {
return incr, nil
}
if fldPath[1] == utils.Accounting {
return ec.getAcountingForPath(fldPath[3:], ec.Accounting[incr.AccountingID])
}
return incr.FieldAsInterface(fldPath)
}
func (ec *EventCost) getRatingForPath(fldPath []string, rating *RatingUnit) (val interface{}, err error) {
if rating == nil {
return nil, utils.ErrNotFound
@@ -933,7 +949,7 @@ func (ec *EventCost) getRatingForPath(fldPath []string, rating *RatingUnit) (val
switch fldPath[0] {
default:
opath, indx := getIndex(fldPath[0])
opath, indx := utils.GetPathIndex(fldPath[0])
if opath != utils.Rates {
return nil, fmt.Errorf("unsupported field prefix: <%s>", opath)
}
@@ -978,3 +994,49 @@ func (ec *EventCost) getRatingForPath(fldPath []string, rating *RatingUnit) (val
}
return rating.FieldAsInterface(fldPath)
}
func (ec *EventCost) getAcountingForPath(fldPath []string, bc *BalanceCharge) (val interface{}, err error) {
if bc == nil {
return nil, utils.ErrNotFound
}
if len(fldPath) == 0 {
return bc, nil
}
if fldPath[0] == utils.Balance {
bl := ec.AccountSummary.BalanceSummaries.BalanceSummaryWithUUD(bc.BalanceUUID)
if bl == nil {
return nil, utils.ErrNotFound
}
if len(fldPath) == 1 {
return bl, nil
}
return bl.FieldAsInterface(fldPath[1:])
}
return bc.FieldAsInterface(fldPath)
}
// String to implement Dataprovider
func (ec *EventCost) String() string {
return utils.ToJSON(ec)
}
// FieldAsString to implement Dataprovider
func (ec *EventCost) FieldAsString(fldPath []string) (string, error) {
ival, err := ec.FieldAsInterface(fldPath)
if err != nil {
return utils.EmptyString, err
}
return utils.IfaceAsString(ival), nil
}
// AsNavigableMap to implement Dataprovider
func (ec *EventCost) AsNavigableMap([]*config.FCTemplate) (*config.NavigableMap, error) {
return nil, utils.ErrNotImplemented
}
// RemoteHost to implement Dataprovider
func (ec *EventCost) RemoteHost() net.Addr {
return utils.LocalAddr()
}

View File

@@ -106,6 +106,7 @@ func (cIl *ChargingInterval) Cost() float64 {
return *cIl.cost
}
// TotalCost returns the cost of charges
func (cIl *ChargingInterval) TotalCost() float64 {
return utils.Round((cIl.Cost() * float64(cIl.CompressFactor)),
globalRoundingDecimals, utils.ROUNDING_MIDDLE)
@@ -131,6 +132,7 @@ type ChargingIncrement struct {
CompressFactor int
}
// Equals returns if the structure has the same value
func (cIt *ChargingIncrement) Equals(oCIt *ChargingIncrement) bool {
return cIt.Usage == oCIt.Usage &&
cIt.Cost == oCIt.Cost &&
@@ -145,6 +147,7 @@ func (cIt *ChargingIncrement) PartiallyEquals(oCIt *ChargingIncrement) bool {
cIt.AccountingID == oCIt.AccountingID
}
// Clone creates a copy of ChargingIncrement
func (cIt *ChargingIncrement) Clone() (cln *ChargingIncrement) {
cln = new(ChargingIncrement)
*cln = *cIt
@@ -156,10 +159,30 @@ func (cIt *ChargingIncrement) TotalUsage() time.Duration {
return time.Duration(cIt.Usage.Nanoseconds() * int64(cIt.CompressFactor))
}
// TotalCost returns the cost of the increment
func (cIt *ChargingIncrement) TotalCost() float64 {
return cIt.Cost * float64(cIt.CompressFactor)
}
// FieldAsInterface func to help EventCost FieldAsInterface
func (cIt *ChargingIncrement) FieldAsInterface(fldPath []string) (val interface{}, err error) {
if len(fldPath) != 1 {
return nil, utils.ErrNotFound
}
switch fldPath[0] {
default:
return nil, fmt.Errorf("unsupported field prefix: <%s>", fldPath[0])
case utils.Usage:
return cIt.Usage, nil
case utils.Cost:
return cIt.Cost, nil
case utils.AccountingID:
return cIt.AccountingID, nil
case utils.CompressFactor:
return cIt.CompressFactor, nil
}
}
// BalanceCharge represents one unit charged to a balance
type BalanceCharge struct {
AccountID string // keep reference for shared balances
@@ -169,6 +192,28 @@ type BalanceCharge struct {
ExtraChargeID string // used in cases when paying *voice with *monetary
}
// FieldAsInterface func to help EventCost FieldAsInterface
func (bc *BalanceCharge) FieldAsInterface(fldPath []string) (val interface{}, err error) {
if len(fldPath) != 1 {
return nil, utils.ErrNotFound
}
switch fldPath[0] {
default:
return nil, fmt.Errorf("unsupported field prefix: <%s>", fldPath[0])
case utils.AccountID:
return bc.AccountID, nil
case utils.BalanceUUID:
return bc.BalanceUUID, nil
case utils.RatingID:
return bc.RatingID, nil
case utils.Units:
return bc.Units, nil
case utils.ExtraChargeID:
return bc.ExtraChargeID, nil
}
}
// Equals returns if the structure have the same fields
func (bc *BalanceCharge) Equals(oBC *BalanceCharge) bool {
bcExtraChargeID := bc.ExtraChargeID
if bcExtraChargeID == "" {
@@ -185,14 +230,17 @@ func (bc *BalanceCharge) Equals(oBC *BalanceCharge) bool {
bcExtraChargeID == oBCExtraChargerID
}
// Clone creates a copy of BalanceCharge
func (bc *BalanceCharge) Clone() *BalanceCharge {
clnBC := new(BalanceCharge)
*clnBC = *bc
return clnBC
}
// RatingMatchedFilters a rating filter
type RatingMatchedFilters map[string]interface{}
// Equals returns if the RatingMatchedFilters are equal
func (rf RatingMatchedFilters) Equals(oRF RatingMatchedFilters) bool {
for k := range rf {
if rf[k] != oRF[k] {
@@ -202,6 +250,7 @@ func (rf RatingMatchedFilters) Equals(oRF RatingMatchedFilters) bool {
return true
}
// Clone creates a copy of RatingMatchedFilters
func (rf RatingMatchedFilters) Clone() (cln map[string]interface{}) {
if rf == nil {
return nil
@@ -213,6 +262,18 @@ func (rf RatingMatchedFilters) Clone() (cln map[string]interface{}) {
return
}
// FieldAsInterface func to help EventCost FieldAsInterface
func (rf *RatingMatchedFilters) FieldAsInterface(fldPath []string) (val interface{}, err error) {
if len(fldPath) != 1 {
return nil, utils.ErrNotFound
}
ct, has := (*rf)[fldPath[0]]
if !has || ct == nil {
return nil, utils.ErrNotFound
}
return ct, nil
}
// ChargedTiming represents one timing attached to a charge
type ChargedTiming struct {
Years utils.Years
@@ -222,6 +283,7 @@ type ChargedTiming struct {
StartTime string
}
// Equals returns if the timings are equal
func (ct *ChargedTiming) Equals(oCT *ChargedTiming) bool {
return ct.Years.Equals(oCT.Years) &&
ct.Months.Equals(oCT.Months) &&
@@ -230,12 +292,34 @@ func (ct *ChargedTiming) Equals(oCT *ChargedTiming) bool {
ct.StartTime == oCT.StartTime
}
// Clone creates a copy of ChargedTiming
func (ct *ChargedTiming) Clone() (cln *ChargedTiming) {
cln = new(ChargedTiming)
*cln = *ct
return
}
// FieldAsInterface func to help EventCost FieldAsInterface
func (ct *ChargedTiming) FieldAsInterface(fldPath []string) (val interface{}, err error) {
if len(fldPath) != 1 {
return nil, utils.ErrNotFound
}
switch fldPath[0] {
default:
return nil, fmt.Errorf("unsupported field prefix: <%s>", fldPath[0])
case utils.YearsFieldName:
return ct.Years, nil
case utils.MonthsFieldName:
return ct.Months, nil
case utils.MonthDaysFieldName:
return ct.MonthDays, nil
case utils.WeekDaysFieldName:
return ct.WeekDays, nil
case utils.StartTime:
return ct.StartTime, nil
}
}
// RatingUnit represents one unit out of RatingPlan matching for an event
type RatingUnit struct {
ConnectFee float64
@@ -248,6 +332,7 @@ type RatingUnit struct {
RatingFiltersID string
}
// Equals returns if RatingUnit is equal to the other
func (ru *RatingUnit) Equals(oRU *RatingUnit) bool {
return ru.ConnectFee == oRU.ConnectFee &&
ru.RoundingMethod == oRU.RoundingMethod &&
@@ -259,15 +344,44 @@ func (ru *RatingUnit) Equals(oRU *RatingUnit) bool {
ru.RatingFiltersID == oRU.RatingFiltersID
}
// Clone creates a copy of RatingUnit
func (ru *RatingUnit) Clone() (cln *RatingUnit) {
cln = new(RatingUnit)
*cln = *ru
return
}
// FieldAsInterface func to help EventCost FieldAsInterface
func (ru *RatingUnit) FieldAsInterface(fldPath []string) (val interface{}, err error) {
if len(fldPath) != 1 {
return nil, utils.ErrNotFound
}
switch fldPath[0] {
default:
return nil, fmt.Errorf("unsupported field prefix: <%s>", fldPath[0])
case utils.ConnectFee:
return ru.ConnectFee, nil
case utils.RoundingMethod:
return ru.RoundingMethod, nil
case utils.RoundingDecimals:
return ru.RoundingDecimals, nil
case utils.MaxCost:
return ru.MaxCost, nil
case utils.MaxCostStrategy:
return ru.MaxCostStrategy, nil
case utils.TimingID:
return ru.TimingID, nil
case utils.RatesID:
return ru.RatesID, nil
case utils.RatingFiltersID:
return ru.RatingFiltersID, nil
}
}
// RatingFilters the map of rating filters
type RatingFilters map[string]RatingMatchedFilters // so we can define search methods
// GetWithSet attempts to retrieve the UUID of a matching data or create a new one
// GetIDWithSet attempts to retrieve the UUID of a matching data or create a new one
func (rfs RatingFilters) GetIDWithSet(rmf RatingMatchedFilters) string {
if rmf == nil || len(rmf) == 0 {
return ""
@@ -283,6 +397,7 @@ func (rfs RatingFilters) GetIDWithSet(rmf RatingMatchedFilters) string {
return uuid
}
// Clone creates a copy of RatingFilters
func (rfs RatingFilters) Clone() (cln RatingFilters) {
cln = make(RatingFilters, len(rfs))
for k, v := range rfs {
@@ -291,6 +406,22 @@ func (rfs RatingFilters) Clone() (cln RatingFilters) {
return
}
// FieldAsInterface func to help EventCost FieldAsInterface
func (rfs *RatingFilters) FieldAsInterface(fldPath []string) (val interface{}, err error) {
if len(fldPath) == 0 {
return nil, utils.ErrNotFound
}
ct, has := (*rfs)[fldPath[0]]
if !has || ct == nil {
return nil, utils.ErrNotFound
}
if len(fldPath) == 1 {
return ct, nil
}
return ct.FieldAsInterface(fldPath[1:])
}
// Rating the map of rating units
type Rating map[string]*RatingUnit
// GetIDWithSet attempts to retrieve the UUID of a matching data or create a new one
@@ -309,6 +440,7 @@ func (crus Rating) GetIDWithSet(cru *RatingUnit) string {
return uuid
}
// Clone creates a copy of Rating
func (crus Rating) Clone() (cln Rating) {
cln = make(Rating, len(crus))
for k, v := range crus {
@@ -317,8 +449,50 @@ func (crus Rating) Clone() (cln Rating) {
return
}
// FieldAsInterface func to help EventCost FieldAsInterface
func (crus *Rating) FieldAsInterface(fldPath []string) (val interface{}, err error) {
if len(fldPath) == 0 {
return nil, utils.ErrNotFound
}
rt, has := (*crus)[fldPath[0]]
if !has || rt == nil {
return nil, utils.ErrNotFound
}
if len(fldPath) == 1 {
return rt, nil
}
return rt.FieldAsInterface(fldPath[1:])
}
// ChargedRates the map with rateGroups
type ChargedRates map[string]RateGroups
// FieldAsInterface func to help EventCost FieldAsInterface
func (crs *ChargedRates) FieldAsInterface(fldPath []string) (val interface{}, err error) {
if len(fldPath) == 0 {
return nil, utils.ErrNotFound
}
opath, indx := utils.GetPathIndex(fldPath[0])
cr, has := (*crs)[opath]
if !has || cr == nil {
return nil, utils.ErrNotFound
}
if indx != nil {
if len(cr) < *indx {
return nil, utils.ErrNotFound
}
rg := cr[*indx]
if len(fldPath) == 1 {
return rg, nil
}
return rg.FieldAsInterface(fldPath[1:])
}
if len(fldPath) != 1 {
return nil, utils.ErrNotFound
}
return cr, nil
}
// GetIDWithSet attempts to retrieve the UUID of a matching data or create a new one
func (crs ChargedRates) GetIDWithSet(rg RateGroups) string {
if rg == nil || len(rg) == 0 {
@@ -335,6 +509,7 @@ func (crs ChargedRates) GetIDWithSet(rg RateGroups) string {
return uuid
}
// Clone creates a copy of ChargedRates
func (crs ChargedRates) Clone() (cln ChargedRates) {
cln = make(ChargedRates, len(crs))
for k, v := range crs {
@@ -343,8 +518,24 @@ func (crs ChargedRates) Clone() (cln ChargedRates) {
return
}
// ChargedTimings the map of ChargedTiming
type ChargedTimings map[string]*ChargedTiming
// FieldAsInterface func to help EventCost FieldAsInterface
func (cts *ChargedTimings) FieldAsInterface(fldPath []string) (val interface{}, err error) {
if len(fldPath) == 0 {
return nil, utils.ErrNotFound
}
ct, has := (*cts)[fldPath[0]]
if !has || ct == nil {
return nil, utils.ErrNotFound
}
if len(fldPath) == 1 {
return ct, nil
}
return ct.FieldAsInterface(fldPath[1:])
}
// GetIDWithSet attempts to retrieve the UUID of a matching data or create a new one
func (cts ChargedTimings) GetIDWithSet(ct *ChargedTiming) string {
if ct == nil {
@@ -361,6 +552,7 @@ func (cts ChargedTimings) GetIDWithSet(ct *ChargedTiming) string {
return uuid
}
// Clone creates a copy of ChargedTimings
func (cts ChargedTimings) Clone() (cln ChargedTimings) {
cln = make(ChargedTimings, len(cts))
for k, v := range cts {
@@ -369,6 +561,7 @@ func (cts ChargedTimings) Clone() (cln ChargedTimings) {
return
}
// Accounting the map of debited balances
type Accounting map[string]*BalanceCharge
// GetIDWithSet attempts to retrieve the UUID of a matching data or create a new one
@@ -387,6 +580,7 @@ func (cbs Accounting) GetIDWithSet(cb *BalanceCharge) string {
return uuid
}
// Clone creates a copy of Accounting
func (cbs Accounting) Clone() (cln Accounting) {
cln = make(Accounting, len(cbs))
for k, v := range cbs {
@@ -395,6 +589,21 @@ func (cbs Accounting) Clone() (cln Accounting) {
return
}
// FieldAsInterface func to help EventCost FieldAsInterface
func (cbs *Accounting) FieldAsInterface(fldPath []string) (val interface{}, err error) {
if len(fldPath) == 0 {
return nil, utils.ErrNotFound
}
ac, has := (*cbs)[fldPath[0]]
if !has || ac == nil {
return nil, utils.ErrNotFound
}
if len(fldPath) == 1 {
return ac, nil
}
return ac.FieldAsInterface(fldPath[1:])
}
// IfaceAsEventCost converts an interface to EventCost
func IfaceAsEventCost(itm interface{}) (ec *EventCost, err error) {
switch otm := itm.(type) {

View File

@@ -233,6 +233,25 @@ type Rate struct {
RateUnit time.Duration
}
// FieldAsInterface func to help EventCost FieldAsInterface
func (r *Rate) FieldAsInterface(fldPath []string) (val interface{}, err error) {
if len(fldPath) != 1 {
return nil, utils.ErrNotFound
}
switch fldPath[0] {
default:
return nil, fmt.Errorf("unsupported field prefix: <%s>", fldPath[0])
case utils.GroupIntervalStart:
return r.GroupIntervalStart, nil
case utils.Value:
return r.Value, nil
case utils.RateIncrement:
return r.RateIncrement, nil
case utils.RateUnit:
return r.RateUnit, nil
}
}
func (r *Rate) Stringify() string {
return utils.Sha1(fmt.Sprintf("%v", r))[:8]
}

View File

@@ -497,6 +497,29 @@ const (
Accounting = "Accounting"
Rating = "Rating"
Charges = "Charges"
CompressFactor = "CompressFactor"
Increments = "Increments"
Balance = "Balance"
BalanceSummaries = "BalanceSummaries"
Type = "Type"
YearsFieldName = "Years"
MonthsFieldName = "Months"
MonthDaysFieldName = "MonthDays"
WeekDaysFieldName = "WeekDays"
GroupIntervalStart = "GroupIntervalStart"
RateIncrement = "RateIncrement"
RateUnit = "RateUnit"
BalanceUUID = "BalanceUUID"
RatingID = "RatingID"
ExtraChargeID = "ExtraChargeID"
ConnectFee = "ConnectFee"
RoundingMethod = "RoundingMethod"
RoundingDecimals = "RoundingDecimals"
MaxCostStrategy = "MaxCostStrategy"
TimingID = "TimingID"
RatesID = "RatesID"
RatingFiltersID = "RatingFiltersID"
AccountingID = "AccountingID"
MetaSessionS = "*sessions"
MetaDefault = "*default"
Error = "Error"

View File

@@ -892,3 +892,23 @@ func CastRPCErr(err error) error {
func RandomInteger(min, max int) int {
return math_rand.Intn(max-min) + min
}
// GetPathIndex returns the path and index if index present
// path[index]=>path,index
// path=>path,nil
func GetPathIndex(spath string) (opath string, idx *int) {
idxStart := strings.Index(spath, IdxStart)
if idxStart == -1 || !strings.HasSuffix(spath, IdxEnd) {
return spath, nil
}
slctr := spath[idxStart+1 : len(spath)-1]
opath = spath[:idxStart]
// if strings.HasPrefix(slctr, DynamicDataPrefix) {
// return
// }
idxVal, err := strconv.Atoi(slctr)
if err != nil {
return spath, nil
}
return opath, &idxVal
}