diff --git a/engine/account.go b/engine/account.go
index 97b014732..ef5d8942f 100644
--- a/engine/account.go
+++ b/engine/account.go
@@ -1084,7 +1084,9 @@ func (acc *Account) Publish(initBal map[string]float64) {
Tenant: acntSummary.Tenant,
ID: utils.GenUUID(),
Time: utils.TimePointer(time.Now()),
- Event: acntSummary.AsMapInterface(),
+ Event: map[string]any{
+ utils.AccountSummary: acntSummary,
+ },
APIOpts: map[string]any{
utils.MetaEventType: utils.AccountUpdate,
},
@@ -1195,29 +1197,6 @@ func (as *AccountSummary) SetInitialValue(old *AccountSummary) {
}
}
-// GetBalanceWithID returns a Balance given balance type and balance ID
-func (acc *Account) GetBalanceWithID(blcType, blcID string) (blc *Balance) {
- for _, blc = range acc.BalanceMap[blcType] {
- if blc.ID == blcID {
- return
- }
- }
- return nil
-}
-
-// FindBalanceByID searches through all balance types for a balance with the
-// specified ID and returns it alongside its type.
-func (acc *Account) FindBalanceByID(balanceID string) (blnc *Balance, blncType string) {
- for balanceType, balances := range acc.BalanceMap {
- for _, balance := range balances {
- if balance.ID == balanceID {
- return balance, balanceType
- }
- }
- }
- return nil, ""
-}
-
// FieldAsInterface func to help EventCost FieldAsInterface
func (as *AccountSummary) FieldAsInterface(fldPath []string) (val any, err error) {
if as == nil || len(fldPath) == 0 {
@@ -1297,6 +1276,61 @@ func (as *AccountSummary) AsMapInterface() map[string]any {
}
}
+// processAccountSummaryField ensures accSummary is an AccountSummary and calls FieldAsInterface on it.
+func processAccountSummaryField(fldPath []string, accSummary any, event map[string]any) (any, error) {
+ var err error
+ var accSummaryBytes []byte
+ switch accSum := accSummary.(type) {
+ case *AccountSummary:
+ // Directly proceed if already *AccountSummary.
+ return accSum.FieldAsInterface(fldPath)
+ case string:
+ // Convert string to bytes for unmarshalling
+ // if it's a serialized *AccountSummary.
+ accSummaryBytes = []byte(accSum)
+ default:
+ // Marshal non-string types to JSON bytes
+ // for unmarshalling into *AccountSummary.
+ accSummaryBytes, err = json.Marshal(accSum)
+ if err != nil {
+ return nil, err
+ }
+ }
+ var as AccountSummary
+ if err = json.Unmarshal(accSummaryBytes, &as); err != nil {
+ return nil, err
+ }
+
+ // Update AccountSummary with the unmarshalled *AccountSummary
+ // to avoid repetitive serialization.
+ event[utils.AccountSummary] = &as
+
+ return as.FieldAsInterface(fldPath)
+}
+
+// GetBalanceWithID returns a Balance given balance type and balance ID
+func (acc *Account) GetBalanceWithID(blcType, blcID string) (blc *Balance) {
+ for _, blc = range acc.BalanceMap[blcType] {
+ if blc.ID == blcID {
+ return
+ }
+ }
+ return nil
+}
+
+// FindBalanceByID searches through all balance types for a balance with the
+// specified ID and returns it alongside its type.
+func (acc *Account) FindBalanceByID(balanceID string) (blnc *Balance, blncType string) {
+ for balanceType, balances := range acc.BalanceMap {
+ for _, balance := range balances {
+ if balance.ID == balanceID {
+ return balance, balanceType
+ }
+ }
+ }
+ return nil, ""
+}
+
func (acc *Account) String() string {
return utils.ToJSON(acc)
}
diff --git a/engine/dynamicdp.go b/engine/dynamicdp.go
index 293a2ddb5..17cfddba5 100644
--- a/engine/dynamicdp.go
+++ b/engine/dynamicdp.go
@@ -19,8 +19,8 @@ along with this program. If not, see
package engine
import (
- "encoding/json"
"fmt"
+ "slices"
"github.com/nyaruka/phonenumbers"
@@ -73,39 +73,19 @@ func (dDP *dynamicDP) FieldAsInterface(fldPath []string) (val any, err error) {
return nil, utils.ErrNotFound
}
- // Parsing deeper than the first level in *req.CostDetails requires it to be
- // of type *EventCost. If not, we serialize and deserialize into an *EventCost.
- if len(fldPath) > 3 &&
- fldPath[0] == utils.MetaReq && fldPath[1] == utils.CostDetails {
+ // Ensure type for supported path elements to allow calling their specific
+ // FieldAsInterface method.
+ if len(fldPath) > 3 && fldPath[0] == utils.MetaReq &&
+ slices.Contains([]string{utils.CostDetails, utils.AccountSummary}, fldPath[1]) {
if mp, canCast := dDP.initialDP.(utils.MapStorage); canCast {
if event, canCast := mp[utils.MetaReq].(map[string]any); canCast {
- if cd, has := event[utils.CostDetails]; has {
- var cdBytes []byte
- switch cd := cd.(type) {
- case *EventCost:
- // Directly proceed if already *EventCost.
- return cd.FieldAsInterface(fldPath[2:])
- case string:
- // Convert string to bytes for unmarshalling
- // if it's a serialized *EventCost.
- cdBytes = []byte(cd)
- default:
- // Marshal non-string types to JSON bytes
- // for unmarshalling into *EventCost.
- cdBytes, err = json.Marshal(cd)
- if err != nil {
- return nil, err
- }
+ if field, has := event[fldPath[1]]; has {
+ switch fldPath[1] {
+ case utils.CostDetails:
+ return processEventCostField(fldPath[2:], field, event)
+ case utils.AccountSummary:
+ return processAccountSummaryField(fldPath[2:], field, event)
}
- var ec EventCost
- if err = json.Unmarshal(cdBytes, &ec); err != nil {
- return nil, err
- }
-
- // Update CostDetails with the unmarshalled *EventCost
- // to avoid repetitive serialization.
- event[utils.CostDetails] = &ec
- return ec.FieldAsInterface(fldPath[2:])
}
}
}
diff --git a/engine/eventcost.go b/engine/eventcost.go
index bd1abda49..a84bb2466 100644
--- a/engine/eventcost.go
+++ b/engine/eventcost.go
@@ -19,6 +19,7 @@ along with this program. If not, see
package engine
import (
+ "encoding/json"
"errors"
"fmt"
"slices"
@@ -1216,3 +1217,35 @@ func (ec *EventCost) Remove(fldPath []string) error {
func (ec *EventCost) GetKeys(nested bool, nesteedLimit int, prefix string) []string {
return nil
}
+
+// processEventCostField ensures cd is an EventCost and calls FieldAsInterface on it.
+func processEventCostField(fldPath []string, cd any, event map[string]any) (any, error) {
+ var err error
+ var cdBytes []byte
+ switch cd := cd.(type) {
+ case *EventCost:
+ // Directly proceed if already *EventCost.
+ return cd.FieldAsInterface(fldPath)
+ case string:
+ // Convert string to bytes for unmarshalling
+ // if it's a serialized *EventCost.
+ cdBytes = []byte(cd)
+ default:
+ // Marshal non-string types to JSON bytes
+ // for unmarshalling into *EventCost.
+ cdBytes, err = json.Marshal(cd)
+ if err != nil {
+ return nil, err
+ }
+ }
+ var ec EventCost
+ if err = json.Unmarshal(cdBytes, &ec); err != nil {
+ return nil, err
+ }
+
+ // Update CostDetails with the unmarshalled *EventCost
+ // to avoid repetitive serialization.
+ event[utils.CostDetails] = &ec
+
+ return ec.FieldAsInterface(fldPath)
+}
diff --git a/general_tests/ees_it_test.go b/general_tests/ees_it_test.go
index 764e3c41e..c1f27723e 100644
--- a/general_tests/ees_it_test.go
+++ b/general_tests/ees_it_test.go
@@ -147,6 +147,8 @@ func TestEEsExportEventChanges(t *testing.T) {
FilterIDs: []string{
"*string:~*req.CostDetails.Charges[0].Increments[0].Accounting.Balance.ID:BALANCE_TEST",
"*string:~*req.CostDetails.Charges[0].Increments[0].Accounting.Balance.Type:*voice",
+ "*string:~*req.AccountSummary.BalanceSummaries.BALANCE_TEST.ID:BALANCE_TEST",
+ "*string:~*req.AccountSummary.BalanceSummaries.BALANCE_TEST.Type:*voice",
},
Path: "*req.BalanceFound",
Type: utils.MetaVariable,
@@ -202,6 +204,15 @@ func TestEEsExportEventChanges(t *testing.T) {
},
},
},
+ utils.AccountSummary: &engine.AccountSummary{
+ BalanceSummaries: engine.BalanceSummaries{
+ {
+ ID: "BALANCE_TEST",
+ Type: utils.MetaVoice,
+ UUID: "123456",
+ },
+ },
+ },
},
},
}