diff --git a/apier/v2/cdre.go b/apier/v2/cdre.go
new file mode 100644
index 000000000..cd3c7ab3a
--- /dev/null
+++ b/apier/v2/cdre.go
@@ -0,0 +1,150 @@
+/*
+Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
+Copyright (C) ITsysCOM GmbH
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see
+*/
+package v2
+
+import (
+ "fmt"
+ "path"
+ "strconv"
+ "strings"
+ "time"
+ "unicode/utf8"
+
+ "github.com/cgrates/cgrates/engine"
+ "github.com/cgrates/cgrates/utils"
+)
+
+type AttrExportCdrsToFile struct {
+ CdrFormat *string // Cdr output file format
+ FieldSeparator *string // Separator used between fields
+ ExportID *string // Optional exportid
+ ExportDirectory *string // If provided it overwrites the configured export directory
+ ExportFileName *string // If provided the output filename will be set to this
+ ExportTemplate *string // Exported fields template <""|fld1,fld2|>
+ DataUsageMultiplyFactor *float64 // Multiply data usage before export (eg: convert from KBytes to Bytes)
+ SMSUsageMultiplyFactor *float64 // Multiply sms usage before export (eg: convert from SMS unit to call duration for some billing systems)
+ MMSUsageMultiplyFactor *float64 // Multiply mms usage before export (eg: convert from MMS unit to call duration for some billing systems)
+ GenericUsageMultiplyFactor *float64 // Multiply generic usage before export (eg: convert from GENERIC unit to call duration for some billing systems)
+ CostMultiplyFactor *float64 // Multiply the cost before export, eg: apply VAT
+ RoundingDecimals *int // force rounding to this value
+ Verbose bool // Disable CgrIds reporting in reply/ExportedCgrIds and reply/UnexportedCgrIds
+ utils.RPCCDRsFilter // Inherit the CDR filter attributes
+}
+
+type ExportedFileCdrs struct {
+ ExportedFilePath string // Full path to the newly generated export file
+ TotalRecords int // Number of CDRs to be exported
+ TotalCost float64 // Sum of all costs in exported CDRs
+ FirstOrderId, LastOrderId int64 // The order id of the last exported CDR
+ ExportedCgrIds []string // List of successfuly exported cgrids in the file
+ UnexportedCgrIds map[string]string // Map of errored CDRs, map key is cgrid, value will be the error string
+}
+
+// Deprecated, please use ApierV1.ExportCDRs instead
+func (self *ApierV2) ExportCdrsToFile(attr AttrExportCdrsToFile, reply *ExportedFileCdrs) (err error) {
+ cdreReloadStruct := <-self.Config.ConfigReloads[utils.CDRE] // Read the content of the channel, locking it
+ defer func() { self.Config.ConfigReloads[utils.CDRE] <- cdreReloadStruct }() // Unlock reloads at exit
+ exportTemplate := self.Config.CdreProfiles[utils.META_DEFAULT]
+ if attr.ExportTemplate != nil && len(*attr.ExportTemplate) != 0 { // Export template prefered, use it
+ var hasIt bool
+ if exportTemplate, hasIt = self.Config.CdreProfiles[*attr.ExportTemplate]; !hasIt {
+ return fmt.Errorf("%s:ExportTemplate", utils.ErrNotFound)
+ }
+ }
+ exportFormat := exportTemplate.ExportFormat
+ if attr.CdrFormat != nil && len(*attr.CdrFormat) != 0 {
+ exportFormat = strings.ToLower(*attr.CdrFormat)
+ }
+ if !utils.IsSliceMember(utils.CDRExportFormats, exportFormat) {
+ return utils.NewErrMandatoryIeMissing("CdrFormat")
+ }
+ fieldSep := exportTemplate.FieldSeparator
+ if attr.FieldSeparator != nil && len(*attr.FieldSeparator) != 0 {
+ fieldSep, _ = utf8.DecodeRuneInString(*attr.FieldSeparator)
+ if fieldSep == utf8.RuneError {
+ return fmt.Errorf("%s:FieldSeparator:%s", utils.ErrServerError, "Invalid")
+ }
+ }
+ eDir := exportTemplate.ExportPath
+ if attr.ExportDirectory != nil && len(*attr.ExportDirectory) != 0 {
+ eDir = *attr.ExportDirectory
+ }
+ exportID := strconv.FormatInt(time.Now().Unix(), 10)
+ if attr.ExportID != nil && len(*attr.ExportID) != 0 {
+ exportID = *attr.ExportID
+ }
+ fileName := fmt.Sprintf("cdre_%s.%s", exportID, exportFormat)
+ if attr.ExportFileName != nil && len(*attr.ExportFileName) != 0 {
+ fileName = *attr.ExportFileName
+ }
+ filePath := path.Join(eDir, fileName)
+ if exportFormat == utils.DRYRUN {
+ filePath = utils.DRYRUN
+ }
+ usageMultiplyFactor := exportTemplate.UsageMultiplyFactor
+ if attr.DataUsageMultiplyFactor != nil && *attr.DataUsageMultiplyFactor != 0.0 {
+ usageMultiplyFactor[utils.DATA] = *attr.DataUsageMultiplyFactor
+ }
+ if attr.SMSUsageMultiplyFactor != nil && *attr.SMSUsageMultiplyFactor != 0.0 {
+ usageMultiplyFactor[utils.SMS] = *attr.SMSUsageMultiplyFactor
+ }
+ if attr.MMSUsageMultiplyFactor != nil && *attr.MMSUsageMultiplyFactor != 0.0 {
+ usageMultiplyFactor[utils.MMS] = *attr.MMSUsageMultiplyFactor
+ }
+ if attr.GenericUsageMultiplyFactor != nil && *attr.GenericUsageMultiplyFactor != 0.0 {
+ usageMultiplyFactor[utils.GENERIC] = *attr.GenericUsageMultiplyFactor
+ }
+ costMultiplyFactor := exportTemplate.CostMultiplyFactor
+ if attr.CostMultiplyFactor != nil && *attr.CostMultiplyFactor != 0.0 {
+ costMultiplyFactor = *attr.CostMultiplyFactor
+ }
+ cdrsFltr, err := attr.RPCCDRsFilter.AsCDRsFilter(self.Config.DefaultTimezone)
+ if err != nil {
+ return utils.NewErrServerError(err)
+ }
+ cdrs, _, err := self.CdrDb.GetCDRs(cdrsFltr, false)
+ if err != nil {
+ return err
+ } else if len(cdrs) == 0 {
+ *reply = ExportedFileCdrs{ExportedFilePath: ""}
+ return nil
+ }
+ roundingDecimals := self.Config.RoundingDecimals
+ if attr.RoundingDecimals != nil {
+ roundingDecimals = *attr.RoundingDecimals
+ }
+ cdrexp, err := engine.NewCDRExporter(cdrs, exportTemplate, exportFormat, filePath, utils.META_NONE, exportID,
+ exportTemplate.Synchronous, exportTemplate.Attempts, fieldSep, usageMultiplyFactor,
+ costMultiplyFactor, roundingDecimals, self.Config.HttpSkipTlsVerify, self.HTTPPoster)
+ if err != nil {
+ return utils.NewErrServerError(err)
+ }
+ if err := cdrexp.ExportCDRs(); err != nil {
+ return utils.NewErrServerError(err)
+ }
+ if cdrexp.TotalExportedCdrs() == 0 {
+ *reply = ExportedFileCdrs{ExportedFilePath: ""}
+ return nil
+ }
+ *reply = ExportedFileCdrs{ExportedFilePath: filePath, TotalRecords: len(cdrs), TotalCost: cdrexp.TotalCost(), FirstOrderId: cdrexp.FirstOrderId(), LastOrderId: cdrexp.LastOrderId()}
+ if !attr.Verbose {
+ reply.ExportedCgrIds = cdrexp.PositiveExports()
+ reply.UnexportedCgrIds = cdrexp.NegativeExports()
+ }
+ return nil
+}