Configurable default_timezone for timestamps which are missing it, fixes #149, fixes #108

This commit is contained in:
DanB
2015-08-19 12:15:28 +02:00
parent 18c3161b8d
commit 1efd09a655
66 changed files with 378 additions and 328 deletions

View File

@@ -41,12 +41,12 @@ type CallCostLog struct {
// Handler for generic cgr cdr http
func cgrCdrHandler(w http.ResponseWriter, r *http.Request) {
cgrCdr, err := NewCgrCdrFromHttpReq(r)
cgrCdr, err := NewCgrCdrFromHttpReq(r, cdrServer.cgrCfg.DefaultTimezone)
if err != nil {
Logger.Err(fmt.Sprintf("<CDRS> Could not create CDR entry: %s", err.Error()))
return
}
if err := cdrServer.processCdr(cgrCdr.AsStoredCdr()); err != nil {
if err := cdrServer.processCdr(cgrCdr.AsStoredCdr(cdrServer.cgrCfg.DefaultTimezone)); err != nil {
Logger.Err(fmt.Sprintf("<CDRS> Errors when storing CDR entry: %s", err.Error()))
}
}
@@ -59,7 +59,7 @@ func fsCdrHandler(w http.ResponseWriter, r *http.Request) {
Logger.Err(fmt.Sprintf("<CDRS> Could not create CDR entry: %s", err.Error()))
return
}
if err := cdrServer.processCdr(fsCdr.AsStoredCdr()); err != nil {
if err := cdrServer.processCdr(fsCdr.AsStoredCdr(cdrServer.Timezone())); err != nil {
Logger.Err(fmt.Sprintf("<CDRS> Errors when storing CDR entry: %s", err.Error()))
}
}
@@ -76,6 +76,10 @@ type CdrServer struct {
guard *GuardianLock
}
func (self *CdrServer) Timezone() string {
return self.cgrCfg.DefaultTimezone
}
func (self *CdrServer) RegisterHanlersToServer(server *Server) {
cdrServer = self // Share the server object for handlers
server.RegisterHttpFunc("/cdr_http", cgrCdrHandler)
@@ -92,7 +96,7 @@ func (self *CdrServer) ProcessExternalCdr(cdr *ExternalCdr) error {
if cdr.Subject == "" { // Use account information as rating subject if missing
cdr.Subject = cdr.Account
}
storedCdr, err := NewStoredCdrFromExternalCdr(cdr)
storedCdr, err := NewStoredCdrFromExternalCdr(cdr, self.cgrCfg.DefaultTimezone)
if err != nil {
return err
}
@@ -246,7 +250,7 @@ func (self *CdrServer) deriveCdrs(storedCdr *StoredCdr) ([]*StoredCdr, error) {
dcSupplFld, _ := utils.NewRSRField(dc.SupplierField)
dcDCausseld, _ := utils.NewRSRField(dc.DisconnectCauseField)
forkedCdr, err := storedCdr.ForkCdr(dc.RunId, dcReqTypeFld, dcDirFld, dcTenantFld, dcCategoryFld, dcAcntFld, dcSubjFld, dcDstFld,
dcSTimeFld, dcPddFld, dcATimeFld, dcDurFld, dcSupplFld, dcDCausseld, []*utils.RSRField{}, true)
dcSTimeFld, dcPddFld, dcATimeFld, dcDurFld, dcSupplFld, dcDCausseld, []*utils.RSRField{}, true, self.cgrCfg.DefaultTimezone)
if err != nil {
Logger.Err(fmt.Sprintf("Could not fork CGR with cgrid %s, run: %s, error: %s", storedCdr.CgrId, dc.RunId, err.Error()))
continue // do not add it to the forked CDR list

View File

@@ -115,8 +115,8 @@ func TestCdrsHttpCdrReplication(t *testing.T) {
} else if len(rcvedCdrs) != 1 {
t.Error("Unexpected number of CDRs returned: ", len(rcvedCdrs))
} else {
rcvSetupTime, _ := utils.ParseTimeDetectLayout(rcvedCdrs[0].SetupTime)
rcvAnswerTime, _ := utils.ParseTimeDetectLayout(rcvedCdrs[0].AnswerTime)
rcvSetupTime, _ := utils.ParseTimeDetectLayout(rcvedCdrs[0].SetupTime, "")
rcvAnswerTime, _ := utils.ParseTimeDetectLayout(rcvedCdrs[0].AnswerTime, "")
//rcvUsage, _ := utils.ParseDurationWithSecs(rcvedCdrs[0].Usage)
if rcvedCdrs[0].CgrId != testCdr1.CgrId ||
rcvedCdrs[0].TOR != testCdr1.TOR ||

View File

@@ -24,7 +24,7 @@ import (
"strconv"
)
func NewCgrCdrFromHttpReq(req *http.Request) (CgrCdr, error) {
func NewCgrCdrFromHttpReq(req *http.Request, timezone string) (CgrCdr, error) {
if req.Form == nil {
if err := req.ParseForm(); err != nil {
return nil, err
@@ -40,11 +40,11 @@ func NewCgrCdrFromHttpReq(req *http.Request) (CgrCdr, error) {
type CgrCdr map[string]string
func (cgrCdr CgrCdr) getCgrId() string {
func (cgrCdr CgrCdr) getCgrId(timezone string) string {
if cgrId, hasIt := cgrCdr[utils.CGRID]; hasIt {
return cgrId
}
setupTime, _ := utils.ParseTimeDetectLayout(cgrCdr[utils.SETUP_TIME])
setupTime, _ := utils.ParseTimeDetectLayout(cgrCdr[utils.SETUP_TIME], timezone)
return utils.Sha1(cgrCdr[utils.ACCID], setupTime.UTC().String())
}
@@ -58,9 +58,9 @@ func (cgrCdr CgrCdr) getExtraFields() map[string]string {
return extraFields
}
func (cgrCdr CgrCdr) AsStoredCdr() *StoredCdr {
func (cgrCdr CgrCdr) AsStoredCdr(timezone string) *StoredCdr {
storCdr := new(StoredCdr)
storCdr.CgrId = cgrCdr.getCgrId()
storCdr.CgrId = cgrCdr.getCgrId(timezone)
storCdr.TOR = cgrCdr[utils.TOR]
storCdr.AccId = cgrCdr[utils.ACCID]
storCdr.CdrHost = cgrCdr[utils.CDRHOST]
@@ -72,9 +72,9 @@ func (cgrCdr CgrCdr) AsStoredCdr() *StoredCdr {
storCdr.Account = cgrCdr[utils.ACCOUNT]
storCdr.Subject = cgrCdr[utils.SUBJECT]
storCdr.Destination = cgrCdr[utils.DESTINATION]
storCdr.SetupTime, _ = utils.ParseTimeDetectLayout(cgrCdr[utils.SETUP_TIME]) // Not interested to process errors, should do them if necessary in a previous step
storCdr.SetupTime, _ = utils.ParseTimeDetectLayout(cgrCdr[utils.SETUP_TIME], timezone) // Not interested to process errors, should do them if necessary in a previous step
storCdr.Pdd, _ = utils.ParseDurationWithSecs(cgrCdr[utils.PDD])
storCdr.AnswerTime, _ = utils.ParseTimeDetectLayout(cgrCdr[utils.ANSWER_TIME])
storCdr.AnswerTime, _ = utils.ParseTimeDetectLayout(cgrCdr[utils.ANSWER_TIME], timezone)
storCdr.Usage, _ = utils.ParseDurationWithSecs(cgrCdr[utils.USAGE])
storCdr.Supplier = cgrCdr[utils.SUPPLIER]
storCdr.DisconnectCause = cgrCdr[utils.DISCONNECT_CAUSE]

View File

@@ -39,14 +39,14 @@ func TestCgrCdrAsStoredCdr(t *testing.T) {
utils.TENANT: "cgrates.org", utils.CATEGORY: "call",
utils.ACCOUNT: "1001", utils.SUBJECT: "1001", utils.DESTINATION: "1002", utils.SETUP_TIME: "2013-11-07T08:42:20Z", utils.ANSWER_TIME: "2013-11-07T08:42:26Z",
utils.USAGE: "10", utils.SUPPLIER: "SUPPL1", "field_extr1": "val_extr1", "fieldextr2": "valextr2"}
setupTime, _ := utils.ParseTimeDetectLayout(cgrCdr["setup_time"])
setupTime, _ := utils.ParseTimeDetectLayout(cgrCdr["setup_time"], "")
expctRtCdr := &StoredCdr{CgrId: utils.Sha1(cgrCdr["accid"], setupTime.String()), TOR: utils.VOICE, AccId: cgrCdr["accid"], CdrHost: cgrCdr["cdrhost"], CdrSource: cgrCdr["cdrsource"],
ReqType: cgrCdr["reqtype"],
Direction: cgrCdr[utils.DIRECTION], Tenant: cgrCdr["tenant"], Category: cgrCdr[utils.CATEGORY], Account: cgrCdr["account"], Subject: cgrCdr["subject"],
Destination: cgrCdr["destination"], SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC),
Usage: time.Duration(10) * time.Second, Supplier: "SUPPL1",
ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: -1}
if storedCdr := cgrCdr.AsStoredCdr(); !reflect.DeepEqual(expctRtCdr, storedCdr) {
if storedCdr := cgrCdr.AsStoredCdr(""); !reflect.DeepEqual(expctRtCdr, storedCdr) {
t.Errorf("Expecting %v, received: %v", expctRtCdr, storedCdr)
}
}
@@ -80,7 +80,7 @@ func TestReplicatedCgrCdrAsStoredCdr(t *testing.T) {
Cost: 0.12,
Rated: true,
}
if storedCdr := cgrCdr.AsStoredCdr(); !reflect.DeepEqual(expctRtCdr, storedCdr) {
if storedCdr := cgrCdr.AsStoredCdr(""); !reflect.DeepEqual(expctRtCdr, storedCdr) {
t.Errorf("Expecting %v, received: %v", expctRtCdr, storedCdr)
}
}

View File

@@ -25,7 +25,7 @@ import (
type Event interface {
GetName() string
GetCgrId() string
GetCgrId(timezone string) string
GetUUID() string
GetSessionIds() []string // Returns identifiers needed to control a session (eg disconnect)
GetDirection(string) string
@@ -36,8 +36,8 @@ type Event interface {
GetCategory(string) string
GetTenant(string) string
GetReqType(string) string
GetSetupTime(string) (time.Time, error)
GetAnswerTime(string) (time.Time, error)
GetSetupTime(string, string) (time.Time, error)
GetAnswerTime(string, string) (time.Time, error)
GetEndTime() (time.Time, error)
GetDuration(string) (time.Duration, error)
GetPdd(string) (time.Duration, error)
@@ -46,9 +46,9 @@ type Event interface {
GetOriginatorIP(string) string
GetExtraFields() map[string]string
MissingParameter() bool
ParseEventValue(*utils.RSRField) string
ParseEventValue(*utils.RSRField, string) string
PassesFieldFilter(*utils.RSRField) (bool, string)
AsStoredCdr() *StoredCdr
AsStoredCdr(timezone string) *StoredCdr
String() string
AsEvent(string) Event
ComputeLcr() bool

View File

@@ -76,8 +76,8 @@ type FSCdr struct {
body map[string]interface{} // keeps the loaded body for extra field search
}
func (fsCdr FSCdr) getCgrId() string {
setupTime, _ := utils.ParseTimeDetectLayout(fsCdr.vars[FS_SETUP_TIME])
func (fsCdr FSCdr) getCgrId(timezone string) string {
setupTime, _ := utils.ParseTimeDetectLayout(fsCdr.vars[FS_SETUP_TIME], timezone)
return utils.Sha1(fsCdr.vars[FS_UUID], setupTime.UTC().String())
}
@@ -124,10 +124,10 @@ func (fsCdr FSCdr) searchExtraField(field string, body map[string]interface{}) (
return
}
func (fsCdr FSCdr) AsStoredCdr() *StoredCdr {
func (fsCdr FSCdr) AsStoredCdr(timezone string) *StoredCdr {
storCdr := new(StoredCdr)
storCdr.CgrId = fsCdr.getCgrId()
storCdr.CgrId = fsCdr.getCgrId(timezone)
storCdr.TOR = utils.VOICE
storCdr.AccId = fsCdr.vars[FS_UUID]
storCdr.CdrHost = fsCdr.vars[FS_IP]
@@ -139,11 +139,11 @@ func (fsCdr FSCdr) AsStoredCdr() *StoredCdr {
storCdr.Account = utils.FirstNonEmpty(fsCdr.vars[FS_ACCOUNT], fsCdr.vars[FS_USERNAME])
storCdr.Subject = utils.FirstNonEmpty(fsCdr.vars[FS_SUBJECT], fsCdr.vars[FS_USERNAME])
storCdr.Destination = utils.FirstNonEmpty(fsCdr.vars[FS_DESTINATION], fsCdr.vars[FS_CALL_DEST_NR], fsCdr.vars[FS_SIP_REQUSER])
storCdr.SetupTime, _ = utils.ParseTimeDetectLayout(fsCdr.vars[FS_SETUP_TIME]) // Not interested to process errors, should do them if necessary in a previous step
storCdr.SetupTime, _ = utils.ParseTimeDetectLayout(fsCdr.vars[FS_SETUP_TIME], timezone) // Not interested to process errors, should do them if necessary in a previous step
pddStr := utils.FirstNonEmpty(fsCdr.vars[FS_PROGRESS_MEDIAMSEC], fsCdr.vars[FS_PROGRESSMS])
pddStr = pddStr + "ms"
storCdr.Pdd, _ = time.ParseDuration(pddStr)
storCdr.AnswerTime, _ = utils.ParseTimeDetectLayout(fsCdr.vars[FS_ANSWER_TIME])
storCdr.AnswerTime, _ = utils.ParseTimeDetectLayout(fsCdr.vars[FS_ANSWER_TIME], timezone)
storCdr.Usage, _ = utils.ParseDurationWithSecs(fsCdr.vars[FS_DURATION])
storCdr.Supplier = fsCdr.vars[utils.CGR_SUPPLIER]
storCdr.DisconnectCause = utils.FirstNonEmpty(fsCdr.vars[utils.CGR_DISCONNECT_CAUSE], fsCdr.vars["hangup_cause"])

View File

@@ -53,13 +53,13 @@ func TestCDRFields(t *testing.T) {
if err != nil {
t.Errorf("Error loading cdr: %v", err)
}
setupTime, _ := utils.ParseTimeDetectLayout("1436280728")
answerTime, _ := utils.ParseTimeDetectLayout("1436280728")
setupTime, _ := utils.ParseTimeDetectLayout("1436280728", "")
answerTime, _ := utils.ParseTimeDetectLayout("1436280728", "")
expctStoredCdr := &StoredCdr{CgrId: "164b0422fdc6a5117031b427439482c6a4f90e41", TOR: utils.VOICE, AccId: "e3133bf7-dcde-4daf-9663-9a79ffcef5ad",
CdrHost: "127.0.0.1", CdrSource: "freeswitch_json", Direction: utils.OUT, Category: "call", ReqType: utils.META_PREPAID, Tenant: "cgrates.org", Account: "1001", Subject: "1001",
Destination: "1003", SetupTime: setupTime, Pdd: time.Duration(28) * time.Millisecond, AnswerTime: answerTime, Usage: time.Duration(66) * time.Second, Supplier: "supplier1",
DisconnectCause: "NORMAL_CLEARING", ExtraFields: map[string]string{"sip_user_agent": "PJSUA v2.3 Linux-3.2.0.4/x86_64/glibc-2.13"}, Cost: -1}
if storedCdr := fsCdr.AsStoredCdr(); !reflect.DeepEqual(expctStoredCdr, storedCdr) {
if storedCdr := fsCdr.AsStoredCdr(""); !reflect.DeepEqual(expctStoredCdr, storedCdr) {
t.Errorf("Expecting: %v, received: %v", expctStoredCdr, storedCdr)
}
}

View File

@@ -60,7 +60,7 @@ type LcrRequest struct {
*utils.Paginator
}
func (self *LcrRequest) AsCallDescriptor() (*CallDescriptor, error) {
func (self *LcrRequest) AsCallDescriptor(timezone string) (*CallDescriptor, error) {
if len(self.Account) == 0 || len(self.Destination) == 0 {
return nil, utils.ErrMandatoryIeMissing
}
@@ -81,7 +81,7 @@ func (self *LcrRequest) AsCallDescriptor() (*CallDescriptor, error) {
var err error
if len(self.SetupTime) == 0 {
timeStart = time.Now()
} else if timeStart, err = utils.ParseTimeDetectLayout(self.SetupTime); err != nil {
} else if timeStart, err = utils.ParseTimeDetectLayout(self.SetupTime, timezone); err != nil {
return nil, err
}
var callDur time.Duration

View File

@@ -216,7 +216,7 @@ func TestLcrRequestAsCallDescriptor(t *testing.T) {
sTime := time.Date(2015, 04, 06, 17, 40, 0, 0, time.UTC)
callDur := time.Duration(1) * time.Minute
lcrReq := &LcrRequest{Account: "2001", SetupTime: sTime.String()}
if _, err := lcrReq.AsCallDescriptor(); err == nil || err != utils.ErrMandatoryIeMissing {
if _, err := lcrReq.AsCallDescriptor(""); err == nil || err != utils.ErrMandatoryIeMissing {
t.Error("Unexpected error received: %v", err)
}
lcrReq = &LcrRequest{Account: "2001", Destination: "2002", SetupTime: sTime.String()}
@@ -230,7 +230,7 @@ func TestLcrRequestAsCallDescriptor(t *testing.T) {
TimeStart: sTime,
TimeEnd: sTime.Add(callDur),
}
if cd, err := lcrReq.AsCallDescriptor(); err != nil {
if cd, err := lcrReq.AsCallDescriptor(""); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eCd, cd) {
t.Errorf("Expected: %+v, received: %+v", eCd, cd)

View File

@@ -90,7 +90,7 @@ func StopStartEngine(cfgPath string, waitEngine int) (*exec.Cmd, error) {
return StartEngine(cfgPath, waitEngine)
}
func LoadTariffPlanFromFolder(tpPath string, ratingDb RatingStorage, accountingDb AccountingStorage) error {
func LoadTariffPlanFromFolder(tpPath, timezone string, ratingDb RatingStorage, accountingDb AccountingStorage) error {
loader := NewTpReader(ratingDb, accountingDb, NewFileCSVStorage(utils.CSV_SEP,
path.Join(tpPath, utils.DESTINATIONS_CSV),
path.Join(tpPath, utils.TIMINGS_CSV),
@@ -106,7 +106,7 @@ func LoadTariffPlanFromFolder(tpPath string, ratingDb RatingStorage, accountingD
path.Join(tpPath, utils.ACCOUNT_ACTIONS_CSV),
path.Join(tpPath, utils.DERIVED_CHARGERS_CSV),
path.Join(tpPath, utils.CDR_STATS_CSV),
path.Join(tpPath, utils.USERS_CSV)), "")
path.Join(tpPath, utils.USERS_CSV)), "", timezone)
if err := loader.LoadAll(); err != nil {
return utils.NewErrServerError(err)
}

View File

@@ -223,7 +223,7 @@ var csvr *TpReader
func init() {
csvr = NewTpReader(ratingStorage, accountingStorage, NewStringCSVStorage(',', destinations, timings, rates, destinationRates, ratingPlans, ratingProfiles,
sharedGroups, lcrs, actions, actionTimings, actionTriggers, accountActions, derivedCharges, cdrStats, users), "")
sharedGroups, lcrs, actions, actionTimings, actionTriggers, accountActions, derivedCharges, cdrStats, users), "", "")
if err := csvr.LoadDestinations(); err != nil {
log.Print("error in LoadDestinations:", err)
}

View File

@@ -131,7 +131,7 @@ func TestLoadFromCSV(t *testing.T) {
path.Join(*dataDir, "tariffplans", *tpCsvScenario, utils.ACCOUNT_ACTIONS_CSV),
path.Join(*dataDir, "tariffplans", *tpCsvScenario, utils.DERIVED_CHARGERS_CSV),
path.Join(*dataDir, "tariffplans", *tpCsvScenario, utils.CDR_STATS_CSV),
path.Join(*dataDir, "tariffplans", *tpCsvScenario, utils.USERS_CSV)), "")
path.Join(*dataDir, "tariffplans", *tpCsvScenario, utils.USERS_CSV)), "", "")
if err = loader.LoadDestinations(); err != nil {
t.Error("Failed loading destinations: ", err.Error())
@@ -201,7 +201,7 @@ func TestLoadFromStorDb(t *testing.T) {
if !*testLocal {
return
}
loader := NewTpReader(ratingDbStor, accountDbStor, storDb, utils.TEST_SQL)
loader := NewTpReader(ratingDbStor, accountDbStor, storDb, utils.TEST_SQL, "")
if err := loader.LoadDestinations(); err != nil {
t.Error("Failed loading destinations: ", err.Error())
}
@@ -244,7 +244,7 @@ func TestLoadIndividualProfiles(t *testing.T) {
if !*testLocal {
return
}
loader := NewTpReader(ratingDbApier, accountDbApier, storDb, utils.TEST_SQL)
loader := NewTpReader(ratingDbApier, accountDbApier, storDb, utils.TEST_SQL, "")
// Load ratingPlans. This will also set destination keys
if ratingPlans, err := storDb.GetTpRatingPlans(utils.TEST_SQL, "", nil); err != nil {
t.Fatal("Could not retrieve rating plans")

View File

@@ -532,7 +532,7 @@ func (tps TpCdrStats) GetCdrStats() (map[string][]*utils.TPCdrStat, error) {
return css, nil
}
func UpdateCdrStats(cs *CdrStats, triggers ActionTriggerPriotityList, tpCs *utils.TPCdrStat) {
func UpdateCdrStats(cs *CdrStats, triggers ActionTriggerPriotityList, tpCs *utils.TPCdrStat, timezone string) {
if tpCs.QueueLength != "" && tpCs.QueueLength != "0" {
if qi, err := strconv.Atoi(tpCs.QueueLength); err == nil {
cs.QueueLength = qi
@@ -560,7 +560,7 @@ func UpdateCdrStats(cs *CdrStats, triggers ActionTriggerPriotityList, tpCs *util
if tpCs.SetupInterval != "" {
times := strings.Split(tpCs.SetupInterval, utils.INFIELD_SEP)
if len(times) > 0 {
if sTime, err := utils.ParseTimeDetectLayout(times[0]); err == nil {
if sTime, err := utils.ParseTimeDetectLayout(times[0], timezone); err == nil {
if len(cs.SetupInterval) < 1 {
cs.SetupInterval = append(cs.SetupInterval, sTime)
} else {
@@ -571,7 +571,7 @@ func UpdateCdrStats(cs *CdrStats, triggers ActionTriggerPriotityList, tpCs *util
}
}
if len(times) > 1 {
if eTime, err := utils.ParseTimeDetectLayout(times[1]); err == nil {
if eTime, err := utils.ParseTimeDetectLayout(times[1], timezone); err == nil {
if len(cs.SetupInterval) < 2 {
cs.SetupInterval = append(cs.SetupInterval, eTime)
} else {

View File

@@ -20,5 +20,5 @@ package engine
// RawCDR is the original CDR received from external sources (eg: FreeSWITCH)
type RawCdr interface {
AsStoredCdr() *StoredCdr // Convert the inbound Cdr into internally used one, CgrCdr
AsStoredCdr(string) *StoredCdr // Convert the inbound Cdr into internally used one, CgrCdr
}

View File

@@ -50,6 +50,7 @@ type Responder struct {
ExitChan chan bool
CdrSrv *CdrServer
Stats StatsInterface
Timezone string
cnt int64
}
@@ -208,7 +209,7 @@ func (rs *Responder) GetDerivedMaxSessionTime(ev *StoredCdr, reply *float64) err
if !matchingAllFilters { // Do not process the derived charger further if not all filters were matched
continue
}
startTime, err := ev.GetSetupTime(utils.META_DEFAULT)
startTime, err := ev.GetSetupTime(utils.META_DEFAULT, rs.Timezone)
if err != nil {
return err
}
@@ -272,7 +273,7 @@ func (rs *Responder) GetSessionRuns(ev *StoredCdr, sRuns *[]*SessionRun) error {
if !utils.IsSliceMember([]string{utils.META_PREPAID, utils.PREPAID}, ev.GetReqType(dc.ReqTypeField)) {
continue // We only consider prepaid sessions
}
startTime, err := ev.GetAnswerTime(dc.AnswerTimeField)
startTime, err := ev.GetAnswerTime(dc.AnswerTimeField, rs.Timezone)
if err != nil {
return errors.New("Error parsing answer event start time")
}

View File

@@ -496,7 +496,7 @@ func TestMySQLSetCdr(t *testing.T) {
utils.ANSWER_TIME: "2013-11-07T08:42:26Z", utils.USAGE: "15s", utils.PDD: "7s", utils.SUPPLIER: "SUPPL1", "field_extr1": "val_extr1", "fieldextr2": "valextr2", "cdrsource": utils.TEST_SQL}
for _, cdr := range []*CgrCdr{cgrCdr1, cgrCdr2, cgrCdr3, cgrCdr4, cgrCdr5} {
if err := mysqlDb.SetCdr(cdr.AsStoredCdr()); err != nil {
if err := mysqlDb.SetCdr(cdr.AsStoredCdr("")); err != nil {
t.Error(err.Error())
}
}
@@ -890,15 +890,15 @@ func TestMySQLRemStoredCdrs(t *testing.T) {
} else if len(storedCdrs) != 7 {
t.Error("Unexpected number of StoredCdrs returned: ", storedCdrs)
}
tm, _ := utils.ParseTimeDetectLayout("2013-11-08T08:42:20Z")
tm, _ := utils.ParseTimeDetectLayout("2013-11-08T08:42:20Z", "")
cgrIdA1 := utils.Sha1("aaa1", tm.String())
tm, _ = utils.ParseTimeDetectLayout("2013-11-08T08:42:22Z")
tm, _ = utils.ParseTimeDetectLayout("2013-11-08T08:42:22Z", "")
cgrIdA2 := utils.Sha1("aaa2", tm.String())
tm, _ = utils.ParseTimeDetectLayout("2013-11-07T08:42:24Z")
tm, _ = utils.ParseTimeDetectLayout("2013-11-07T08:42:24Z", "")
cgrIdA3 := utils.Sha1("aaa3", tm.String())
tm, _ = utils.ParseTimeDetectLayout("2013-11-07T08:42:21Z")
tm, _ = utils.ParseTimeDetectLayout("2013-11-07T08:42:21Z", "")
cgrIdA4 := utils.Sha1("aaa4", tm.String())
tm, _ = utils.ParseTimeDetectLayout("2013-11-07T08:42:25Z")
tm, _ = utils.ParseTimeDetectLayout("2013-11-07T08:42:25Z", "")
cgrIdA5 := utils.Sha1("aaa5", tm.String())
cgrIdB2 := utils.Sha1("bbb2", time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC).String())
cgrIdB3 := utils.Sha1("bbb3", time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC).String())

View File

@@ -491,7 +491,7 @@ func TestPSQLSetCdr(t *testing.T) {
utils.ANSWER_TIME: "2013-11-07T08:42:26Z", utils.USAGE: "15s", utils.PDD: "7s", utils.SUPPLIER: "SUPPL1", "field_extr1": "val_extr1", "fieldextr2": "valextr2", "cdrsource": utils.TEST_SQL}
for _, cdr := range []*CgrCdr{cgrCdr1, cgrCdr2, cgrCdr3, cgrCdr4, cgrCdr5} {
if err := psqlDb.SetCdr(cdr.AsStoredCdr()); err != nil {
if err := psqlDb.SetCdr(cdr.AsStoredCdr("")); err != nil {
t.Error(err.Error())
}
}
@@ -885,15 +885,15 @@ func TestPSQLRemStoredCdrs(t *testing.T) {
} else if len(storedCdrs) != 7 {
t.Error("Unexpected number of StoredCdrs returned: ", storedCdrs)
}
tm, _ := utils.ParseTimeDetectLayout("2013-11-08T08:42:20Z")
tm, _ := utils.ParseTimeDetectLayout("2013-11-08T08:42:20Z", "")
cgrIdA1 := utils.Sha1("aaa1", tm.String())
tm, _ = utils.ParseTimeDetectLayout("2013-11-08T08:42:22Z")
tm, _ = utils.ParseTimeDetectLayout("2013-11-08T08:42:22Z", "")
cgrIdA2 := utils.Sha1("aaa2", tm.String())
tm, _ = utils.ParseTimeDetectLayout("2013-11-07T08:42:24Z")
tm, _ = utils.ParseTimeDetectLayout("2013-11-07T08:42:24Z", "")
cgrIdA3 := utils.Sha1("aaa3", tm.String())
tm, _ = utils.ParseTimeDetectLayout("2013-11-07T08:42:21Z")
tm, _ = utils.ParseTimeDetectLayout("2013-11-07T08:42:21Z", "")
cgrIdA4 := utils.Sha1("aaa4", tm.String())
tm, _ = utils.ParseTimeDetectLayout("2013-11-07T08:42:25Z")
tm, _ = utils.ParseTimeDetectLayout("2013-11-07T08:42:25Z", "")
cgrIdA5 := utils.Sha1("aaa5", tm.String())
cgrIdB2 := utils.Sha1("bbb2", time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC).String())
cgrIdB3 := utils.Sha1("bbb3", time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC).String())

View File

@@ -29,19 +29,19 @@ import (
"github.com/cgrates/cgrates/utils"
)
func NewStoredCdrFromExternalCdr(extCdr *ExternalCdr) (*StoredCdr, error) {
func NewStoredCdrFromExternalCdr(extCdr *ExternalCdr, timezone string) (*StoredCdr, error) {
var err error
storedCdr := &StoredCdr{CgrId: extCdr.CgrId, OrderId: extCdr.OrderId, TOR: extCdr.TOR, AccId: extCdr.AccId, CdrHost: extCdr.CdrHost, CdrSource: extCdr.CdrSource,
ReqType: extCdr.ReqType, Direction: extCdr.Direction, Tenant: extCdr.Tenant, Category: extCdr.Category, Account: extCdr.Account, Subject: extCdr.Subject,
Destination: extCdr.Destination, Supplier: extCdr.Supplier, DisconnectCause: extCdr.DisconnectCause,
MediationRunId: extCdr.MediationRunId, RatedAccount: extCdr.RatedAccount, RatedSubject: extCdr.RatedSubject, Cost: extCdr.Cost, Rated: extCdr.Rated}
if storedCdr.SetupTime, err = utils.ParseTimeDetectLayout(extCdr.SetupTime); err != nil {
if storedCdr.SetupTime, err = utils.ParseTimeDetectLayout(extCdr.SetupTime, timezone); err != nil {
return nil, err
}
if len(storedCdr.CgrId) == 0 { // Populate CgrId if not present
storedCdr.CgrId = utils.Sha1(storedCdr.AccId, storedCdr.SetupTime.String())
}
if storedCdr.AnswerTime, err = utils.ParseTimeDetectLayout(extCdr.AnswerTime); err != nil {
if storedCdr.AnswerTime, err = utils.ParseTimeDetectLayout(extCdr.AnswerTime, timezone); err != nil {
return nil, err
}
if storedCdr.Usage, err = utils.ParseDurationWithSecs(extCdr.Usage); err != nil {
@@ -209,7 +209,7 @@ func (storedCdr *StoredCdr) PassesFieldFilter(fieldFilter *utils.RSRField) (bool
return false, ""
}
func (storedCdr *StoredCdr) AsStoredCdr() *StoredCdr {
func (storedCdr *StoredCdr) AsStoredCdr(timezone string) *StoredCdr {
return storedCdr
}
@@ -259,7 +259,7 @@ func (storedCdr *StoredCdr) AsHttpForm() url.Values {
// Used in mediation, primaryMandatory marks whether missing field out of request represents error or can be ignored
func (storedCdr *StoredCdr) ForkCdr(runId string, reqTypeFld, directionFld, tenantFld, categFld, accountFld, subjectFld, destFld, setupTimeFld, pddFld,
answerTimeFld, durationFld, supplierFld, disconnectCauseFld *utils.RSRField,
extraFlds []*utils.RSRField, primaryMandatory bool) (*StoredCdr, error) {
extraFlds []*utils.RSRField, primaryMandatory bool, timezone string) (*StoredCdr, error) {
if reqTypeFld == nil {
reqTypeFld, _ = utils.NewRSRField(utils.META_DEFAULT)
}
@@ -378,13 +378,13 @@ func (storedCdr *StoredCdr) ForkCdr(runId string, reqTypeFld, directionFld, tena
sTimeStr := storedCdr.FieldAsString(setupTimeFld)
if primaryMandatory && len(sTimeStr) == 0 {
return nil, utils.NewErrMandatoryIeMissing(utils.SETUP_TIME, setupTimeFld.Id)
} else if frkStorCdr.SetupTime, err = utils.ParseTimeDetectLayout(sTimeStr); err != nil {
} else if frkStorCdr.SetupTime, err = utils.ParseTimeDetectLayout(sTimeStr, timezone); err != nil {
return nil, err
}
aTimeStr := storedCdr.FieldAsString(answerTimeFld)
if primaryMandatory && len(aTimeStr) == 0 {
return nil, utils.NewErrMandatoryIeMissing(utils.ANSWER_TIME, answerTimeFld.Id)
} else if frkStorCdr.AnswerTime, err = utils.ParseTimeDetectLayout(aTimeStr); err != nil {
} else if frkStorCdr.AnswerTime, err = utils.ParseTimeDetectLayout(aTimeStr, timezone); err != nil {
return nil, err
}
durStr := storedCdr.FieldAsString(durationFld)
@@ -447,7 +447,7 @@ func (storedCdr *StoredCdr) ComputeLcr() bool {
func (storedCdr *StoredCdr) GetName() string {
return storedCdr.CdrSource
}
func (storedCdr *StoredCdr) GetCgrId() string {
func (storedCdr *StoredCdr) GetCgrId(timezone string) string {
return storedCdr.CgrId
}
func (storedCdr *StoredCdr) GetUUID() string {
@@ -528,7 +528,7 @@ func (storedCdr *StoredCdr) GetReqType(fieldName string) string {
}
return storedCdr.FieldAsString(&utils.RSRField{Id: fieldName})
}
func (storedCdr *StoredCdr) GetSetupTime(fieldName string) (time.Time, error) {
func (storedCdr *StoredCdr) GetSetupTime(fieldName, timezone string) (time.Time, error) {
if utils.IsSliceMember([]string{utils.SETUP_TIME, utils.META_DEFAULT}, fieldName) {
return storedCdr.SetupTime, nil
}
@@ -538,9 +538,9 @@ func (storedCdr *StoredCdr) GetSetupTime(fieldName string) (time.Time, error) {
} else {
sTimeVal = storedCdr.FieldAsString(&utils.RSRField{Id: fieldName})
}
return utils.ParseTimeDetectLayout(sTimeVal)
return utils.ParseTimeDetectLayout(sTimeVal, timezone)
}
func (storedCdr *StoredCdr) GetAnswerTime(fieldName string) (time.Time, error) {
func (storedCdr *StoredCdr) GetAnswerTime(fieldName, timezone string) (time.Time, error) {
if utils.IsSliceMember([]string{utils.ANSWER_TIME, utils.META_DEFAULT}, fieldName) {
return storedCdr.AnswerTime, nil
}
@@ -550,7 +550,7 @@ func (storedCdr *StoredCdr) GetAnswerTime(fieldName string) (time.Time, error) {
} else {
aTimeVal = storedCdr.FieldAsString(&utils.RSRField{Id: fieldName})
}
return utils.ParseTimeDetectLayout(aTimeVal)
return utils.ParseTimeDetectLayout(aTimeVal, timezone)
}
func (storedCdr *StoredCdr) GetEndTime() (time.Time, error) {
return storedCdr.AnswerTime.Add(storedCdr.Usage), nil
@@ -607,7 +607,7 @@ func (storedCdr *StoredCdr) MissingParameter() bool {
len(storedCdr.Account) == 0 ||
len(storedCdr.Destination) == 0
}
func (storedCdr *StoredCdr) ParseEventValue(rsrFld *utils.RSRField) string {
func (storedCdr *StoredCdr) ParseEventValue(rsrFld *utils.RSRField, timezone string) string {
return storedCdr.FieldAsString(rsrFld)
}
func (storedCdr *StoredCdr) String() string {
@@ -660,14 +660,14 @@ type UsageRecord struct {
ExtraFields map[string]string
}
func (self *UsageRecord) AsStoredCdr() (*StoredCdr, error) {
func (self *UsageRecord) AsStoredCdr(timezone string) (*StoredCdr, error) {
var err error
storedCdr := &StoredCdr{TOR: self.TOR, ReqType: self.ReqType, Direction: self.Direction, Tenant: self.Tenant, Category: self.Category,
Account: self.Account, Subject: self.Subject, Destination: self.Destination}
if storedCdr.SetupTime, err = utils.ParseTimeDetectLayout(self.SetupTime); err != nil {
if storedCdr.SetupTime, err = utils.ParseTimeDetectLayout(self.SetupTime, timezone); err != nil {
return nil, err
}
if storedCdr.AnswerTime, err = utils.ParseTimeDetectLayout(self.AnswerTime); err != nil {
if storedCdr.AnswerTime, err = utils.ParseTimeDetectLayout(self.AnswerTime, timezone); err != nil {
return nil, err
}
if storedCdr.Usage, err = utils.ParseDurationWithSecs(self.Usage); err != nil {
@@ -682,7 +682,7 @@ func (self *UsageRecord) AsStoredCdr() (*StoredCdr, error) {
return storedCdr, nil
}
func (self *UsageRecord) AsCallDescriptor() (*CallDescriptor, error) {
func (self *UsageRecord) AsCallDescriptor(timezone string) (*CallDescriptor, error) {
var err error
cd := &CallDescriptor{
TOR: self.TOR,
@@ -697,7 +697,7 @@ func (self *UsageRecord) AsCallDescriptor() (*CallDescriptor, error) {
if len(timeStr) == 0 { // In case of auth, answer time will not be defined, so take it out of setup one
timeStr = self.SetupTime
}
if cd.TimeStart, err = utils.ParseTimeDetectLayout(timeStr); err != nil {
if cd.TimeStart, err = utils.ParseTimeDetectLayout(timeStr, timezone); err != nil {
return nil, err
}
if usage, err := utils.ParseDurationWithSecs(self.Usage); err != nil {

View File

@@ -45,7 +45,7 @@ func TestNewStoredCdrFromExternalCdr(t *testing.T) {
SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), MediationRunId: utils.DEFAULT_RUNID,
Usage: time.Duration(10), Pdd: time.Duration(7) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, RatedAccount: "dan", RatedSubject: "dans", Rated: true,
}
if storedCdr, err := NewStoredCdrFromExternalCdr(extCdr); err != nil {
if storedCdr, err := NewStoredCdrFromExternalCdr(extCdr, ""); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eStorCdr, storedCdr) {
t.Errorf("Expected: %+v, received: %+v", eStorCdr, storedCdr)
@@ -357,7 +357,7 @@ func TestStoredCdrForkCdr(t *testing.T) {
&utils.RSRField{Id: utils.CATEGORY}, &utils.RSRField{Id: utils.ACCOUNT}, &utils.RSRField{Id: utils.SUBJECT}, &utils.RSRField{Id: utils.DESTINATION},
&utils.RSRField{Id: utils.SETUP_TIME}, &utils.RSRField{Id: utils.PDD}, &utils.RSRField{Id: utils.ANSWER_TIME}, &utils.RSRField{Id: utils.USAGE},
&utils.RSRField{Id: utils.SUPPLIER}, &utils.RSRField{Id: utils.DISCONNECT_CAUSE},
[]*utils.RSRField{&utils.RSRField{Id: "field_extr1"}, &utils.RSRField{Id: "field_extr2"}}, true)
[]*utils.RSRField{&utils.RSRField{Id: "field_extr1"}, &utils.RSRField{Id: "field_extr2"}}, true, "")
if err != nil {
t.Error("Unexpected error received", err)
}
@@ -391,7 +391,7 @@ func TestStoredCdrForkCdrStaticVals(t *testing.T) {
rsrStDCause, _ := utils.NewRSRField("^HANGUP_COMPLETE")
rsrPdd, _ := utils.NewRSRField("^3")
rtCdrOut2, err := storCdr.ForkCdr("wholesale_run", rsrStPostpaid, rsrStIn, rsrStCgr, rsrStPC, rsrStFA, rsrStFS, &utils.RSRField{Id: "destination"},
rsrStST, rsrPdd, rsrStAT, rsrStDur, rsrStSuppl, rsrStDCause, []*utils.RSRField{}, true)
rsrStST, rsrPdd, rsrStAT, rsrStDur, rsrStSuppl, rsrStDCause, []*utils.RSRField{}, true, "")
if err != nil {
t.Error("Unexpected error received", err)
}
@@ -407,7 +407,7 @@ func TestStoredCdrForkCdrStaticVals(t *testing.T) {
_, err = storCdr.ForkCdr("wholesale_run", &utils.RSRField{Id: "dummy_header"}, &utils.RSRField{Id: "direction"}, &utils.RSRField{Id: "tenant"},
&utils.RSRField{Id: "tor"}, &utils.RSRField{Id: "account"}, &utils.RSRField{Id: "subject"}, &utils.RSRField{Id: "destination"},
&utils.RSRField{Id: "setup_time"}, &utils.RSRField{Id: utils.PDD}, &utils.RSRField{Id: "answer_time"}, &utils.RSRField{Id: "duration"}, &utils.RSRField{Id: utils.SUPPLIER},
&utils.RSRField{Id: utils.DISCONNECT_CAUSE}, []*utils.RSRField{}, true)
&utils.RSRField{Id: utils.DISCONNECT_CAUSE}, []*utils.RSRField{}, true, "")
if err == nil {
t.Error("Failed to detect missing header")
}
@@ -429,7 +429,7 @@ func TestStoredCdrForkCdrFromMetaDefaults(t *testing.T) {
&utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT},
&utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT},
&utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT},
[]*utils.RSRField{&utils.RSRField{Id: "field_extr1"}, &utils.RSRField{Id: "fieldextr2"}}, true)
[]*utils.RSRField{&utils.RSRField{Id: "field_extr1"}, &utils.RSRField{Id: "fieldextr2"}}, true, "")
if err != nil {
t.Fatal("Unexpected error received", err)
}
@@ -439,7 +439,7 @@ func TestStoredCdrForkCdrFromMetaDefaults(t *testing.T) {
}
// Should also accept nil as defaults
if cdrOut, err := storCdr.ForkCdr("wholesale_run", nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
[]*utils.RSRField{&utils.RSRField{Id: "field_extr1"}, &utils.RSRField{Id: "fieldextr2"}}, true); err != nil {
[]*utils.RSRField{&utils.RSRField{Id: "field_extr1"}, &utils.RSRField{Id: "fieldextr2"}}, true, ""); err != nil {
t.Fatal("Unexpected error received", err)
} else if !reflect.DeepEqual(expctCdr, cdrOut) {
t.Errorf("Expected: %v, received: %v", expctCdr, cdrOut)
@@ -479,7 +479,7 @@ func TestStoredCdrEventFields(t *testing.T) {
if res := cdr.GetName(); res != "test" {
t.Error("Received: ", res)
}
if res := cdr.GetCgrId(); res != utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()) {
if res := cdr.GetCgrId(""); res != utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()) {
t.Error("Received: ", res)
}
if res := cdr.GetUUID(); res != "dsafdsaf" {
@@ -509,10 +509,10 @@ func TestStoredCdrEventFields(t *testing.T) {
if res := cdr.GetReqType(utils.META_DEFAULT); res != utils.META_RATED {
t.Error("Received: ", res)
}
if st, _ := cdr.GetSetupTime(utils.META_DEFAULT); st != time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC) {
if st, _ := cdr.GetSetupTime(utils.META_DEFAULT, ""); st != time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC) {
t.Error("Received: ", st)
}
if at, _ := cdr.GetAnswerTime(utils.META_DEFAULT); at != time.Date(2013, 11, 7, 8, 42, 27, 0, time.UTC) {
if at, _ := cdr.GetAnswerTime(utils.META_DEFAULT, ""); at != time.Date(2013, 11, 7, 8, 42, 27, 0, time.UTC) {
t.Error("Received: ", at)
}
if et, _ := cdr.GetEndTime(); et != time.Date(2013, 11, 7, 8, 42, 37, 0, time.UTC) {
@@ -540,7 +540,7 @@ func TesUsageReqAsStoredCdr(t *testing.T) {
eStorCdr := &StoredCdr{TOR: utils.VOICE, ReqType: utils.META_RATED, Direction: "*out",
Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002",
SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), Usage: time.Duration(10)}
if storedCdr, err := setupReq.AsStoredCdr(); err != nil {
if storedCdr, err := setupReq.AsStoredCdr(""); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eStorCdr, storedCdr) {
t.Errorf("Expected: %+v, received: %+v", eStorCdr, storedCdr)
@@ -555,7 +555,7 @@ func TestUsageReqAsCD(t *testing.T) {
eCD := &CallDescriptor{TOR: req.TOR, Direction: req.Direction,
Tenant: req.Tenant, Category: req.Category, Account: req.Account, Subject: req.Subject, Destination: req.Destination,
TimeStart: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), TimeEnd: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).Add(time.Duration(10))}
if cd, err := req.AsCallDescriptor(); err != nil {
if cd, err := req.AsCallDescriptor(""); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eCD, cd) {
t.Errorf("Expected: %+v, received: %+v", eCD, cd)

View File

@@ -12,6 +12,7 @@ import (
type TpReader struct {
tpid string
timezone string
ratingStorage RatingStorage
accountingStorage AccountingStorage
lr LoadReader
@@ -36,9 +37,10 @@ type TpReader struct {
users map[string]*UserProfile
}
func NewTpReader(rs RatingStorage, as AccountingStorage, lr LoadReader, tpid string) *TpReader {
func NewTpReader(rs RatingStorage, as AccountingStorage, lr LoadReader, tpid, timezone string) *TpReader {
tpr := &TpReader{
tpid: tpid,
timezone: timezone,
ratingStorage: rs,
accountingStorage: as,
lr: lr,
@@ -456,7 +458,7 @@ func (tpr *TpReader) LoadLCRs() (err error) {
}
}
tag := utils.LCRKey(tpLcr.Direction, tpLcr.Tenant, tpLcr.Category, tpLcr.Account, tpLcr.Subject)
activationTime, _ := utils.ParseTimeDetectLayout(tpLcr.ActivationTime)
activationTime, _ := utils.ParseTimeDetectLayout(tpLcr.ActivationTime, tpr.timezone)
lcr, found := tpr.lcrs[tag]
if !found {
@@ -611,7 +613,7 @@ func (tpr *TpReader) LoadActionTriggers() (err error) {
for key, atrsLst := range storAts {
atrs := make([]*ActionTrigger, len(atrsLst))
for idx, atr := range atrsLst {
balanceExpirationDate, _ := utils.ParseTimeDetectLayout(atr.BalanceExpirationDate)
balanceExpirationDate, _ := utils.ParseTimeDetectLayout(atr.BalanceExpirationDate, tpr.timezone)
id := atr.Id
if id == "" {
id = utils.GenUUID()
@@ -989,7 +991,7 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) {
// only return error if there was something there for the tag
return fmt.Errorf("could not get action triggers for cdr stats id %s: %s", cs.Id, triggerTag)
}
UpdateCdrStats(cs, triggers, tpStat)
UpdateCdrStats(cs, triggers, tpStat, tpr.timezone)
tpr.cdrStats[tag] = cs
}
}