diff --git a/cdrc/csv.go b/cdrc/csv.go index a73f550c8..8dd932c1c 100644 --- a/cdrc/csv.go +++ b/cdrc/csv.go @@ -161,12 +161,22 @@ func (self *CsvRecordsProcessor) recordToStoredCdr(record []string, cdrcCfg *con } var fieldVal string switch cdrFldCfg.Type { - case utils.META_COMPOSED, utils.MetaUnixTimestamp: + case utils.META_COMPOSED: out, err := cdrFldCfg.Value.ParseDataProvider(csvProvider) if err != nil { return nil, err } fieldVal = out + case utils.MetaUnixTimestamp: + out, err := cdrFldCfg.Value.ParseDataProvider(csvProvider) + if err != nil { + return nil, err + } + t, err := utils.ParseTimeDetectLayout(out, self.timezone) + if err != nil { + return nil, err + } + fieldVal += strconv.Itoa(int(t.Unix())) case utils.META_HTTP_POST: lazyHttpFields = append(lazyHttpFields, cdrFldCfg) // Will process later so we can send an estimation of storedCdr to http server default: diff --git a/cdrc/partial_cdr.go b/cdrc/partial_cdr.go index 9a5aa5aba..4fbd361a6 100644 --- a/cdrc/partial_cdr.go +++ b/cdrc/partial_cdr.go @@ -38,9 +38,13 @@ const ( PartialRecordsSuffix = "partial" ) -func NewPartialRecordsCache(ttl time.Duration, expiryAction string, cdrOutDir string, csvSep rune, roundDecimals int, timezone string, httpSkipTlsCheck bool, cdrs rpcclient.RpcClientConnection) (*PartialRecordsCache, error) { - return &PartialRecordsCache{ttl: ttl, expiryAction: expiryAction, cdrOutDir: cdrOutDir, csvSep: csvSep, roundDecimals: roundDecimals, timezone: timezone, httpSkipTlsCheck: httpSkipTlsCheck, cdrs: cdrs, - partialRecords: make(map[string]*PartialCDRRecord), dumpTimers: make(map[string]*time.Timer), guard: guardian.Guardian}, nil +func NewPartialRecordsCache(ttl time.Duration, expiryAction string, cdrOutDir string, csvSep rune, + roundDecimals int, timezone string, httpSkipTlsCheck bool, cdrs rpcclient.RpcClientConnection) (*PartialRecordsCache, error) { + return &PartialRecordsCache{ttl: ttl, expiryAction: expiryAction, cdrOutDir: cdrOutDir, + csvSep: csvSep, roundDecimals: roundDecimals, timezone: timezone, + httpSkipTlsCheck: httpSkipTlsCheck, cdrs: cdrs, + partialRecords: make(map[string]*PartialCDRRecord), + dumpTimers: make(map[string]*time.Timer), guard: guardian.Guardian}, nil } type PartialRecordsCache struct { diff --git a/data/conf/samples/cdrc_partcsv/cgrates.json b/data/conf/samples/cdrc_partcsv/cgrates.json index 2f4f3c969..5525f7e57 100644 --- a/data/conf/samples/cdrc_partcsv/cgrates.json +++ b/data/conf/samples/cdrc_partcsv/cgrates.json @@ -32,7 +32,7 @@ "partial_record_cache": "1s", // duration to cache partial records when not pairing "partial_cache_expiry_action": "*dump_to_file", "content_fields":[ // import content_fields template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value - {"id": "TOR", "field_id": "ToR", "type": "*composed", "value": "^*voice", "mandatory": true}, + {"id": "TOR", "field_id": "ToR", "type": "*composed", "value": "*voice", "mandatory": true}, {"id": "AccId1", "field_id": "OriginID", "type": "*composed", "value": "~0"}, {"id": "AccId2", "field_id": "OriginID", "type": "*composed", "value": "_"}, {"id": "AccId3", "field_id": "OriginID", "type": "*composed", "value": "~1"}, @@ -48,7 +48,7 @@ {"id": "SetupTime", "field_id": "SetupTime", "type": "*composed", "value": "~4", "mandatory": true}, {"id": "AnswerTime", "field_id": "AnswerTime", "type": "*composed", "value": "~4", "mandatory": true}, {"id": "Usage", "field_id": "Usage", "type": "*composed", "value": "~6:s/^(\\d+)$/${1}s/", "mandatory": true}, - {"id": "Partial", "field_id": "Partial", "type": "*composed", "value": "true", "field_filter": "10(partial)"}, + {"id": "Partial", "field_id": "Partial", "type": "*composed", "value": "true", "filters":["*string:10:partial"]}, ], "cache_dump_fields": [ {"tag": "OriginID", "type": "*composed", "value": "OriginID"}, @@ -72,23 +72,23 @@ "partial_record_cache": "1s", // duration to cache partial records when not pairing "partial_cache_expiry_action": "*post_cdr", "content_fields":[ // import content_fields template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value - {"id": "TOR", "field_id": "ToR", "type": "*composed", "value": "^*voice", "mandatory": true}, - {"id": "AccId1", "field_id": "OriginID", "type": "*composed", "value": "0"}, - {"id": "AccId2", "field_id": "OriginID", "type": "*composed", "value": "^_"}, - {"id": "AccId3", "field_id": "OriginID", "type": "*composed", "value": "1"}, - {"id": "AccId4", "field_id": "OriginID", "type": "*composed", "value": "^_"}, - {"id": "AccId5", "field_id": "OriginID", "type": "*composed", "value": "4"}, - {"id": "OrderID", "field_id": "OrderID", "type": "*unix_timestamp", "value": "3"}, - {"id": "RequestType", "field_id": "RequestType", "type": "*composed", "value": "^*rated", "mandatory": true}, - {"id": "Direction", "field_id": "Direction", "type": "*composed", "value": "^*out", "mandatory": true}, - {"id": "Tenant", "field_id": "Tenant", "type": "*composed", "value": "^cgrates.org", "mandatory": true}, - {"id": "Category", "field_id": "Category", "type": "*composed", "value": "^call", "mandatory": true}, + {"id": "TOR", "field_id": "ToR", "type": "*composed", "value": "*voice", "mandatory": true}, + {"id": "AccId1", "field_id": "OriginID", "type": "*composed", "value": "~0"}, + {"id": "AccId2", "field_id": "OriginID", "type": "*composed", "value": "_"}, + {"id": "AccId3", "field_id": "OriginID", "type": "*composed", "value": "~1"}, + {"id": "AccId4", "field_id": "OriginID", "type": "*composed", "value": "_"}, + {"id": "AccId5", "field_id": "OriginID", "type": "*composed", "value": "~4"}, + {"id": "OrderID", "field_id": "OrderID", "type": "*unix_timestamp", "value": "~3"}, + {"id": "RequestType", "field_id": "RequestType", "type": "*composed", "value": "*rated", "mandatory": true}, + {"id": "Direction", "field_id": "Direction", "type": "*composed", "value": "*out", "mandatory": true}, + {"id": "Tenant", "field_id": "Tenant", "type": "*composed", "value": "cgrates.org", "mandatory": true}, + {"id": "Category", "field_id": "Category", "type": "*composed", "value": "call", "mandatory": true}, {"id": "Account", "field_id": "Account", "type": "*composed", "value": "~0:s/^49([1-9]\\d+)$/0$1/", "mandatory": true}, {"id": "Destination", "field_id": "Destination", "type": "*composed", "value": "~1:s/^00(\\d+)$/+$1/", "mandatory": true}, - {"id": "SetupTime", "field_id": "SetupTime", "type": "*composed", "value": "4", "mandatory": true}, - {"id": "AnswerTime", "field_id": "AnswerTime", "type": "*composed", "value": "4", "mandatory": true}, + {"id": "SetupTime", "field_id": "SetupTime", "type": "*composed", "value": "~4", "mandatory": true}, + {"id": "AnswerTime", "field_id": "AnswerTime", "type": "*composed", "value": "~4", "mandatory": true}, {"id": "Usage", "field_id": "Usage", "type": "*composed", "value": "~6:s/^(\\d+)$/${1}s/", "mandatory": true}, - {"id": "Partial", "field_id": "Partial", "type": "*composed", "value": "^true", "field_filter": "10(partial)"}, + {"id": "Partial", "field_id": "Partial", "type": "*composed", "value": "true", "filters":["*string:10:partial"]}, ], }, ], diff --git a/engine/cdr.go b/engine/cdr.go index e1bdbcc27..085109514 100644 --- a/engine/cdr.go +++ b/engine/cdr.go @@ -571,7 +571,8 @@ func (cdr *CDR) formatField(cfgFld *config.CfgCdrField, httpSkipTlsCheck bool, // Used in place where we need to export the CDR based on an export template // ExportRecord is a []string to keep it compatible with encoding/csv Writer -func (cdr *CDR) AsExportRecord(exportFields []*config.CfgCdrField, httpSkipTlsCheck bool, groupedCDRs []*CDR, roundingDecs int) (expRecord []string, err error) { +func (cdr *CDR) AsExportRecord(exportFields []*config.CfgCdrField, + httpSkipTlsCheck bool, groupedCDRs []*CDR, roundingDecs int) (expRecord []string, err error) { for _, cfgFld := range exportFields { if roundingDecs != 0 { clnFld := new(config.CfgCdrField) // Clone so we can modify the rounding decimals without affecting the template