From fece61093e646b14ab6c2459c63f0957a00d3570 Mon Sep 17 00:00:00 2001 From: DanB Date: Mon, 29 Aug 2016 14:28:14 +0200 Subject: [PATCH] AccountSummary inside CDR object, decoupling it from CallCost for error cases --- data/storage/mysql/create_cdrs_tables.sql | 1 + data/storage/postgres/create_cdrs_tables.sql | 1 + engine/account.go | 7 ++++++ engine/account_test.go | 8 +++++++ engine/callcost.go | 1 - engine/cdr.go | 9 +++---- engine/cdrs.go | 25 ++++++++++++-------- engine/models.go | 1 + engine/storage_sql.go | 7 ++++++ utils/utils_test.go | 6 +++++ 10 files changed, 51 insertions(+), 15 deletions(-) diff --git a/data/storage/mysql/create_cdrs_tables.sql b/data/storage/mysql/create_cdrs_tables.sql index c6e86d496..6a77260b1 100644 --- a/data/storage/mysql/create_cdrs_tables.sql +++ b/data/storage/mysql/create_cdrs_tables.sql @@ -28,6 +28,7 @@ CREATE TABLE cdrs ( cost_source varchar(64) NOT NULL, cost DECIMAL(20,4) NOT NULL, cost_details text, + account_summary text, extra_info text, created_at TIMESTAMP NULL, updated_at TIMESTAMP NULL, diff --git a/data/storage/postgres/create_cdrs_tables.sql b/data/storage/postgres/create_cdrs_tables.sql index ed9751694..4ced7df21 100644 --- a/data/storage/postgres/create_cdrs_tables.sql +++ b/data/storage/postgres/create_cdrs_tables.sql @@ -28,6 +28,7 @@ CREATE TABLE cdrs ( cost_source VARCHAR(64) NOT NULL, cost NUMERIC(20,4) DEFAULT NULL, cost_details jsonb, + account_summary jsonb, extra_info text, created_at TIMESTAMP, updated_at TIMESTAMP NULL, diff --git a/engine/account.go b/engine/account.go index 418b316ce..b75806088 100644 --- a/engine/account.go +++ b/engine/account.go @@ -1036,6 +1036,13 @@ func (acc *Account) AsAccountSummary() *AccountSummary { return ad } +func NewAccountSummaryFromJSON(jsn string) (acntSummary *AccountSummary, err error) { + if !utils.IsSliceMember([]string{"", "null"}, jsn) { // Unmarshal only when content + json.Unmarshal([]byte(jsn), &acntSummary) + } + return +} + // AccountDigest contains compressed information about an Account type AccountSummary struct { Tenant string diff --git a/engine/account_test.go b/engine/account_test.go index 1110557df..79609a0fa 100644 --- a/engine/account_test.go +++ b/engine/account_test.go @@ -1873,6 +1873,14 @@ func TestAccountGetBalancesForPrefixMixedBad(t *testing.T) { } } +func TestAccountNewAccountSummaryFromJSON(t *testing.T) { + if acnt, err := NewAccountSummaryFromJSON("null"); err != nil { + t.Error(err) + } else if acnt != nil { + t.Errorf("Expecting nil, received: %+v", acnt) + } +} + func TestAccountAsAccountDigest(t *testing.T) { acnt1 := &Account{ ID: "cgrates.org:account1", diff --git a/engine/callcost.go b/engine/callcost.go index 787723b96..97bf4e780 100644 --- a/engine/callcost.go +++ b/engine/callcost.go @@ -30,7 +30,6 @@ type CallCost struct { Cost float64 Timespans TimeSpans RatedUsage float64 - AccountSummary *AccountSummary deductConnectFee bool negativeConnectFee bool // the connect fee went negative on default balance maxCostDisconect bool diff --git a/engine/cdr.go b/engine/cdr.go index bd890eea3..44d47d419 100644 --- a/engine/cdr.go +++ b/engine/cdr.go @@ -103,10 +103,11 @@ type CDR struct { ExtraFields map[string]string // Extra fields to be stored in CDR CostSource string // The source of this cost Cost float64 - CostDetails *CallCost // Attach the cost details to CDR when possible - ExtraInfo string // Container for extra information related to this CDR, eg: populated with error reason in case of error on calculation - Rated bool // Mark the CDR as rated so we do not process it during rating - Partial bool // Used for partial record processing by CDRC + CostDetails *CallCost // Attach the cost details to CDR when possible + AccountSummary *AccountSummary // Store AccountSummary information + ExtraInfo string // Container for extra information related to this CDR, eg: populated with error reason in case of error on calculation + Rated bool // Mark the CDR as rated so we do not process it during rating + Partial bool // Used for partial record processing by CDRC } func (cdr *CDR) CostDetailsJson() string { diff --git a/engine/cdrs.go b/engine/cdrs.go index 5b5f70d59..46c28d8c0 100644 --- a/engine/cdrs.go +++ b/engine/cdrs.go @@ -239,22 +239,27 @@ func (self *CdrServer) deriveRateStoreStatsReplicate(cdr *CDR, store, stats, rep } } } + // Store AccountSummary if requested + if self.cgrCfg.CDRScdrAccountSummary { + for _, ratedCDR := range ratedCDRs { + if utils.IsSliceMember([]string{utils.META_PREPAID, utils.PREPAID, utils.META_PSEUDOPREPAID, utils.PSEUDOPREPAID, + utils.META_POSTPAID, utils.POSTPAID}, ratedCDR.RequestType) { + acntID := utils.ConcatenatedKey(ratedCDR.Tenant, ratedCDR.Account) + acnt, err := self.dataDB.GetAccount(acntID) + if err != nil { + utils.Logger.Err(fmt.Sprintf(" Querying AccountDigest for account: %s got error: %s", acntID, err.Error())) + } else if acnt.ID != "" { + ratedCDR.AccountSummary = acnt.AsAccountSummary() + } + } + } + } // Store rated CDRs if store { for _, ratedCDR := range ratedCDRs { if ratedCDR.CostDetails != nil { ratedCDR.CostDetails.UpdateCost() ratedCDR.CostDetails.UpdateRatedUsage() - if utils.IsSliceMember([]string{utils.META_PREPAID, utils.PREPAID, utils.META_PSEUDOPREPAID, utils.PSEUDOPREPAID, - utils.META_POSTPAID, utils.POSTPAID}, ratedCDR.RequestType) && self.cgrCfg.CDRScdrAccountSummary { - acntID := utils.ConcatenatedKey(ratedCDR.Tenant, ratedCDR.Account) - acnt, err := self.dataDB.GetAccount(acntID) - if err != nil { - utils.Logger.Err(fmt.Sprintf(" Querying AccountDigest for account: %s got error: %s", acntID, err.Error())) - } else if acnt.ID != "" { - ratedCDR.CostDetails.AccountSummary = acnt.AsAccountSummary() - } - } } if err := self.cdrDb.SetCDR(ratedCDR, true); err != nil { utils.Logger.Err(fmt.Sprintf(" Storing rated CDR %+v, got error: %s", ratedCDR, err.Error())) diff --git a/engine/models.go b/engine/models.go index 1cfc36dc6..4ed27356d 100644 --- a/engine/models.go +++ b/engine/models.go @@ -423,6 +423,7 @@ type TBLCDRs struct { Cost float64 CostDetails string CostSource string + AccountSummary string ExtraInfo string CreatedAt time.Time UpdatedAt time.Time diff --git a/engine/storage_sql.go b/engine/storage_sql.go index f1769bff7..f96f5ba84 100644 --- a/engine/storage_sql.go +++ b/engine/storage_sql.go @@ -675,6 +675,7 @@ func (self *SQLStorage) SetCDR(cdr *CDR, allowUpdate bool) error { CostSource: cdr.CostSource, Cost: cdr.Cost, CostDetails: cdr.CostDetailsJson(), + AccountSummary: utils.ToJSON(cdr.AccountSummary), ExtraInfo: cdr.ExtraInfo, CreatedAt: time.Now(), }) @@ -707,6 +708,7 @@ func (self *SQLStorage) SetCDR(cdr *CDR, allowUpdate bool) error { CostSource: cdr.CostSource, Cost: cdr.Cost, CostDetails: cdr.CostDetailsJson(), + AccountSummary: utils.ToJSON(cdr.AccountSummary), ExtraInfo: cdr.ExtraInfo, UpdatedAt: time.Now(), }, @@ -975,6 +977,10 @@ func (self *SQLStorage) GetCDRs(qryFltr *utils.CDRsFilter, remove bool) ([]*CDR, return nil, 0, fmt.Errorf("JSON unmarshal callcost error for cgrid: %s, runid: %v, error: %s", result.Cgrid, result.RunID, err.Error()) } } + acntSummary, err := NewAccountSummaryFromJSON(result.AccountSummary) + if err != nil { + return nil, 0, fmt.Errorf("JSON unmarshal account summary error for cgrid: %s, runid: %v, error: %s", result.Cgrid, result.RunID, err.Error()) + } usageDur := time.Duration(result.Usage * utils.NANO_MULTIPLIER) pddDur := time.Duration(result.Pdd * utils.NANO_MULTIPLIER) storCdr := &CDR{ @@ -1002,6 +1008,7 @@ func (self *SQLStorage) GetCDRs(qryFltr *utils.CDRsFilter, remove bool) ([]*CDR, CostSource: result.CostSource, Cost: result.Cost, CostDetails: &callCost, + AccountSummary: acntSummary, ExtraInfo: result.ExtraInfo, } cdrs = append(cdrs, storCdr) diff --git a/utils/utils_test.go b/utils/utils_test.go index 0b3190512..35d354c13 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -703,3 +703,9 @@ func TestMaskSuffix(t *testing.T) { } } + +func TestToJSON(t *testing.T) { + if outNilObj := ToJSON(nil); outNilObj != "null" { + t.Errorf("Expecting null, received: <%q>", outNilObj) + } +}