diff --git a/apier/v1/cdrs.go b/apier/v1/cdrs.go
index f92659924..ce9ceaf9f 100644
--- a/apier/v1/cdrs.go
+++ b/apier/v1/cdrs.go
@@ -45,7 +45,7 @@ func (self *ApierV1) ExportCdrsToFile(attr utils.AttrExpFileCdrs, reply *utils.E
return err
}
}
- cdrs, err := self.CdrDb.GetRatedCdrs(tStart, tEnd)
+ cdrs, err := self.CdrDb.GetStoredCdrs(tStart, tEnd, attr.SkipErrors, attr.SkipRated)
if err != nil {
return err
}
@@ -71,7 +71,7 @@ func (self *ApierV1) ExportCdrsToFile(attr utils.AttrExpFileCdrs, reply *utils.E
for idx, cdr := range cdrs {
cgrIds[idx] = cdr.CgrId
}
- if err := self.CdrDb.RemRatedCdrs(cgrIds); err != nil {
+ if err := self.CdrDb.RemStoredCdrs(cgrIds); err != nil {
return err
}
}
diff --git a/apier/v1/mediation.go b/apier/v1/mediation.go
new file mode 100644
index 000000000..fbe648c28
--- /dev/null
+++ b/apier/v1/mediation.go
@@ -0,0 +1,53 @@
+/*
+Rating system designed to be used in VoIP Carriers World
+Copyright (C) 2013 ITsysCOM
+
+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 apier
+
+import (
+ "fmt"
+ "time"
+ "github.com/cgrates/cgrates/utils"
+ "github.com/cgrates/cgrates/mediator"
+)
+
+type MediatorV1 struct {
+ Mediator *mediator.Mediator
+}
+
+// Remotely start mediation with specific runid, runs asynchronously, it's status will be displayed in syslog
+func (self *MediatorV1) RateCdrs(attrs utils.AttrRateCdrs, reply *string) error {
+ if self.Mediator == nil {
+ return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, "MediatorNotRunning")
+ }
+ var tStart, tEnd time.Time
+ var err error
+ if len(attrs.TimeStart) != 0 {
+ if tStart, err = utils.ParseTimeDetectLayout(attrs.TimeStart); err != nil {
+ return err
+ }
+ }
+ if len(attrs.TimeEnd) != 0 {
+ if tEnd, err = utils.ParseTimeDetectLayout(attrs.TimeEnd); err != nil {
+ return err
+ }
+ }
+ if err := self.Mediator.RateCdrs(tStart, tEnd, attrs.RerateErrors, attrs.RerateRated); err != nil {
+ return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error())
+ }
+ return nil
+}
diff --git a/cdrc/cdrc.go b/cdrc/cdrc.go
index 6710dbd5f..d24b815ae 100644
--- a/cdrc/cdrc.go
+++ b/cdrc/cdrc.go
@@ -117,8 +117,8 @@ func (self *Cdrc) parseFieldsConfig() error {
}
// Takes the record out of csv and turns it into http form which can be posted
-func (self *Cdrc) recordAsRatedCdr(record []string) (*utils.RatedCDR, error) {
- ratedCdr := &utils.RatedCDR{CdrSource: self.cgrCfg.CdrcSourceId, ExtraFields: map[string]string{}, Cost: -1}
+func (self *Cdrc) recordAsStoredCdr(record []string) (*utils.StoredCdr, error) {
+ ratedCdr := &utils.StoredCdr{CdrSource: self.cgrCfg.CdrcSourceId, ExtraFields: map[string]string{}, Cost: -1}
var err error
for cfgFieldName, cfgFieldVal := range self.cfgCdrFields {
var fieldVal string
@@ -228,7 +228,7 @@ func (self *Cdrc) processFile(filePath string) error {
engine.Logger.Err(fmt.Sprintf(" Error in csv file: %s", err.Error()))
continue // Other csv related errors, ignore
}
- rawCdr, err := self.recordAsRatedCdr(record)
+ rawCdr, err := self.recordAsStoredCdr(record)
if err != nil {
engine.Logger.Err(fmt.Sprintf(" Error in csv file: %s", err.Error()))
continue
diff --git a/cdrc/cdrc_test.go b/cdrc/cdrc_test.go
index 4678b1a74..0a35613f1 100644
--- a/cdrc/cdrc_test.go
+++ b/cdrc/cdrc_test.go
@@ -56,7 +56,7 @@ func TestParseFieldsConfig(t *testing.T) {
}
}
-func TestRecordAsRatedCdr(t *testing.T) {
+func TestRecordAsStoredCdr(t *testing.T) {
cgrConfig, _ := config.NewDefaultCGRConfig()
cgrConfig.CdrcExtraFields = []string{"supplier:10"}
cdrc := &Cdrc{cgrCfg: cgrConfig}
@@ -64,16 +64,16 @@ func TestRecordAsRatedCdr(t *testing.T) {
t.Error("Failed parsing default fieldIndexesFromConfig", err)
}
cdrRow := []string{"firstField", "secondField"}
- _, err := cdrc.recordAsRatedCdr(cdrRow)
+ _, err := cdrc.recordAsStoredCdr(cdrRow)
if err == nil {
t.Error("Failed to corectly detect missing fields from record")
}
cdrRow = []string{"acc1", "prepaid", "*out", "cgrates.org", "call", "1001", "1001", "+4986517174963", "2013-02-03 19:54:00", "62", "supplier1", "172.16.1.1"}
- rtCdr, err := cdrc.recordAsRatedCdr(cdrRow)
+ rtCdr, err := cdrc.recordAsStoredCdr(cdrRow)
if err != nil {
t.Error("Failed to parse CDR in rated cdr", err)
}
- expectedCdr := &utils.RatedCDR{
+ expectedCdr := &utils.StoredCdr{
CgrId: utils.FSCgrId(cdrRow[0]),
AccId: cdrRow[0],
CdrSource: cgrConfig.CdrcSourceId,
diff --git a/cdrexporter/cdrexporter.go b/cdrexporter/cdrexporter.go
index f5915cff5..466a9268a 100644
--- a/cdrexporter/cdrexporter.go
+++ b/cdrexporter/cdrexporter.go
@@ -23,6 +23,6 @@ import (
)
type CdrWriter interface {
- Write(cdr utils.RatedCDR) string
+ Write(cdr *utils.StoredCdr) string
Close()
}
diff --git a/cdrexporter/csv.go b/cdrexporter/csv.go
index dde1535b9..3bfb47903 100644
--- a/cdrexporter/csv.go
+++ b/cdrexporter/csv.go
@@ -36,7 +36,7 @@ func NewCsvCdrWriter(writer io.Writer, roundDecimals int, extraFields []string)
return &CsvCdrWriter{csv.NewWriter(writer), roundDecimals, extraFields}
}
-func (dcw *CsvCdrWriter) Write(cdr *utils.RatedCDR) error {
+func (dcw *CsvCdrWriter) Write(cdr *utils.StoredCdr) error {
primaryFields := []string{cdr.CgrId, cdr.MediationRunId, cdr.AccId, cdr.CdrHost, cdr.ReqType, cdr.Direction, cdr.Tenant, cdr.TOR, cdr.Account, cdr.Subject,
cdr.Destination, cdr.AnswerTime.String(), strconv.Itoa(int(cdr.Duration)), strconv.FormatFloat(cdr.Cost, 'f', dcw.roundDecimals, 64)}
if len(dcw.extraFields) == 0 {
diff --git a/cdrexporter/csv_test.go b/cdrexporter/csv_test.go
index 0b518b0c1..1b06019b2 100644
--- a/cdrexporter/csv_test.go
+++ b/cdrexporter/csv_test.go
@@ -29,7 +29,7 @@ import (
func TestCsvCdrWriter(t *testing.T) {
writer := &bytes.Buffer{}
csvCdrWriter := NewCsvCdrWriter(writer, 4, []string{"extra3", "extra1"})
- ratedCdr := &utils.RatedCDR{CgrId: utils.FSCgrId("dsafdsaf"), AccId: "dsafdsaf", CdrHost: "192.168.1.1", ReqType: "rated", Direction: "*out", Tenant: "cgrates.org",
+ ratedCdr := &utils.StoredCdr{CgrId: utils.FSCgrId("dsafdsaf"), AccId: "dsafdsaf", CdrHost: "192.168.1.1", ReqType: "rated", Direction: "*out", Tenant: "cgrates.org",
TOR: "call", Account: "1001", Subject: "1001", Destination: "1002", AnswerTime: time.Unix(1383813746, 0).UTC(), Duration: 10, MediationRunId: utils.DEFAULT_RUNID,
ExtraFields: map[string]string{"extra1": "val_extra1", "extra2": "val_extra2", "extra3": "val_extra3"}, Cost: 1.01,
}
diff --git a/cdrs/cdrs.go b/cdrs/cdrs.go
index 05acc22bc..19721c9e8 100644
--- a/cdrs/cdrs.go
+++ b/cdrs/cdrs.go
@@ -42,7 +42,7 @@ func storeAndMediate(rawCdr utils.RawCDR) error {
}
if cfg.CDRSMediator == utils.INTERNAL {
go func() {
- if err := medi.MediateRawCDR(rawCdr); err != nil {
+ if err := medi.RateCdr(rawCdr); err != nil {
engine.Logger.Err(fmt.Sprintf("Could not run mediation on CDR: %s", err.Error()))
}
}()
diff --git a/cdrs/fscdr.go b/cdrs/fscdr.go
index 2981aea4a..ef2989c6c 100644
--- a/cdrs/fscdr.go
+++ b/cdrs/fscdr.go
@@ -162,14 +162,14 @@ func (fsCdr FSCdr) Restore(input string) error {
}
// Used in extra mediation
-func (fsCdr FSCdr) AsRatedCdr(runId, reqTypeFld, directionFld, tenantFld, torFld, accountFld, subjectFld, destFld, answerTimeFld, durationFld string, extraFlds []string, fieldsMandatory bool) (*utils.RatedCDR, error) {
+func (fsCdr FSCdr) AsStoredCdr(runId, reqTypeFld, directionFld, tenantFld, torFld, accountFld, subjectFld, destFld, answerTimeFld, durationFld string, extraFlds []string, fieldsMandatory bool) (*utils.StoredCdr, error) {
if utils.IsSliceMember([]string{runId, reqTypeFld, directionFld, tenantFld, torFld, accountFld, subjectFld, destFld, answerTimeFld, durationFld}, "") {
return nil, errors.New(fmt.Sprintf("%s:FieldName", utils.ERR_MANDATORY_IE_MISSING)) // All input field names are mandatory
}
var err error
var hasKey bool
var aTimeStr, durStr string
- rtCdr := new(utils.RatedCDR)
+ rtCdr := new(utils.StoredCdr)
rtCdr.MediationRunId = runId
rtCdr.Cost = -1.0 // Default for non-rated CDR
if rtCdr.AccId = fsCdr.GetAccId(); len(rtCdr.AccId) == 0 {
diff --git a/cdrs/fscdr_test.go b/cdrs/fscdr_test.go
index b6ca2c473..c7d3dbb2f 100644
--- a/cdrs/fscdr_test.go
+++ b/cdrs/fscdr_test.go
@@ -94,18 +94,18 @@ func TestCDRFields(t *testing.T) {
}
-func TestFsCdrAsRatedCdr(t *testing.T) {
+func TestFsCdrAsStoredCdr(t *testing.T) {
cfg, _ = config.NewDefaultCGRConfig()
fsCdr, err := new(FSCdr).New(body)
if err != nil {
t.Errorf("Error loading cdr: %v", err)
}
- rtCdrOut, err := fsCdr.AsRatedCdr("wholesale_run", "^"+utils.RATED, "^*out", "cgr_tenant", "cgr_tor", "cgr_account", "cgr_subject", "cgr_destination",
+ rtCdrOut, err := fsCdr.AsStoredCdr("wholesale_run", "^"+utils.RATED, "^*out", "cgr_tenant", "cgr_tor", "cgr_account", "cgr_subject", "cgr_destination",
"answer_epoch", "billsec", []string{"effective_caller_id_number"}, true)
if err != nil {
t.Error("Unexpected error received", err)
}
- expctRatedCdr := &utils.RatedCDR{CgrId: utils.FSCgrId("01df56f4-d99a-4ef6-b7fe-b924b2415b7f"), AccId: "01df56f4-d99a-4ef6-b7fe-b924b2415b7f",
+ expctRatedCdr := &utils.StoredCdr{CgrId: utils.FSCgrId("01df56f4-d99a-4ef6-b7fe-b924b2415b7f"), AccId: "01df56f4-d99a-4ef6-b7fe-b924b2415b7f",
CdrHost: "127.0.0.1", CdrSource: FS_CDR_SOURCE, ReqType: utils.RATED,
Direction: "*out", Tenant: "ipbx.itsyscom.com", TOR: "call", Account: "dan", Subject: "dan", Destination: "+4986517174963",
AnswerTime: time.Date(2013, 8, 4, 9, 50, 56, 0, time.UTC).Local(), Duration: time.Duration(4) * time.Second,
@@ -113,12 +113,12 @@ func TestFsCdrAsRatedCdr(t *testing.T) {
if !reflect.DeepEqual(rtCdrOut, expctRatedCdr) {
t.Errorf("Received: %v, expected: %v", rtCdrOut, expctRatedCdr)
}
- rtCdrOut2, err := fsCdr.AsRatedCdr("wholesale_run", "^postpaid", "^*in", "^cgrates.com", "^premium_call", "^first_account", "^first_subject", "cgr_destination",
+ rtCdrOut2, err := fsCdr.AsStoredCdr("wholesale_run", "^postpaid", "^*in", "^cgrates.com", "^premium_call", "^first_account", "^first_subject", "cgr_destination",
"^2013-12-07T08:42:26Z", "^12s", []string{"effective_caller_id_number"}, true)
if err != nil {
t.Error("Unexpected error received", err)
}
- expctRatedCdr2 := &utils.RatedCDR{CgrId: utils.FSCgrId("01df56f4-d99a-4ef6-b7fe-b924b2415b7f"), AccId: "01df56f4-d99a-4ef6-b7fe-b924b2415b7f", CdrHost: "127.0.0.1",
+ expctRatedCdr2 := &utils.StoredCdr{CgrId: utils.FSCgrId("01df56f4-d99a-4ef6-b7fe-b924b2415b7f"), AccId: "01df56f4-d99a-4ef6-b7fe-b924b2415b7f", CdrHost: "127.0.0.1",
CdrSource: FS_CDR_SOURCE, ReqType: "postpaid",
Direction: "*in", Tenant: "cgrates.com", TOR: "premium_call", Account: "first_account", Subject: "first_subject", Destination: "+4986517174963",
AnswerTime: time.Date(2013, 12, 7, 8, 42, 26, 0, time.UTC), Duration: time.Duration(12) * time.Second,
@@ -126,7 +126,7 @@ func TestFsCdrAsRatedCdr(t *testing.T) {
if !reflect.DeepEqual(rtCdrOut2, expctRatedCdr2) {
t.Errorf("Received: %v, expected: %v", rtCdrOut2, expctRatedCdr2)
}
- _, err = fsCdr.AsRatedCdr("wholesale_run", "dummy_header", "direction", "tenant", "tor", "account", "subject", "destination", "answer_time", "duration", []string{"field_extr1", "fieldextr2"}, true)
+ _, err = fsCdr.AsStoredCdr("wholesale_run", "dummy_header", "direction", "tenant", "tor", "account", "subject", "destination", "answer_time", "duration", []string{"field_extr1", "fieldextr2"}, true)
if err == nil {
t.Error("Failed to detect missing header")
}
diff --git a/cmd/cgr-engine/cgr-engine.go b/cmd/cgr-engine/cgr-engine.go
index dae6cde74..ed656c23c 100644
--- a/cmd/cgr-engine/cgr-engine.go
+++ b/cmd/cgr-engine/cgr-engine.go
@@ -117,9 +117,25 @@ func startMediator(responder *engine.Responder, loggerDb engine.LogStorage, cdrD
exitChan <- true
return
}
+ engine.Logger.Info("Registering Mediator RPC service.")
+ server.RpcRegister(&apier.MediatorV1{Mediator: medi})
+
close(chanDone)
}
+// In case of internal mediator apier needs to wait for it to initialize before offering it's methods
+func registerApier(waitOnChans []chan struct{}) {
+ for _, chn := range waitOnChans {
+ select {
+ case <-time.After(5 * time.Minute):
+ engine.Logger.Crit(fmt.Sprintf(" Timeout waiting for dependecies to start."))
+ exitChan <- true
+ return
+ case <-chn:
+ }
+ }
+ }
+
func startCdrc(cdrsChan chan struct{}) {
if cfg.CdrcCdrs == utils.INTERNAL {
<-cdrsChan // Wait for CDRServer to come up before start processing
@@ -200,7 +216,14 @@ func startHistoryServer(chanDone chan struct{}) {
func startHistoryAgent(scribeServer history.Scribe, chanServerStarted chan struct{}) {
if cfg.HistoryServer == utils.INTERNAL { // For internal requests, wait for server to come online before connecting
engine.Logger.Crit(fmt.Sprintf(" Connecting internally to HistoryServer"))
- <-chanServerStarted // If server is not enabled, will have deadlock here
+ select {
+ case <-time.After(1 * time.Minute):
+ engine.Logger.Crit(fmt.Sprintf(" Timeout waiting for server to start."))
+ exitChan <- true
+ return
+ case <-chanServerStarted:
+ }
+ //<-chanServerStarted // If server is not enabled, will have deadlock here
} else { // Connect in iteration since there are chances of concurrency here
for i := 0; i < 3; i++ { //ToDo: Make it globally configurable
//engine.Logger.Crit(fmt.Sprintf(" Trying to connect, iteration: %d, time %s", i, time.Now()))
@@ -376,13 +399,13 @@ func main() {
apier := &apier.ApierV1{StorDb: loadDb, RatingDb: ratingDb, AccountDb: accountDb, CdrDb: cdrDb, Config: cfg}
if cfg.RaterEnabled && !cfg.BalancerEnabled && cfg.RaterBalancer != utils.INTERNAL {
- engine.Logger.Info("Registering CGRateS Rater service")
+ engine.Logger.Info("Registering Rater service")
server.RpcRegister(responder)
server.RpcRegister(apier)
}
if cfg.BalancerEnabled {
- engine.Logger.Info("Registering CGRateS Balancer service.")
+ engine.Logger.Info("Registering Balancer service.")
go stopBalancerSignalHandler()
stopHandled = true
responder.Bal = bal
diff --git a/data/storage/mysql/create_costdetails_tables.sql b/data/storage/mysql/create_costdetails_tables.sql
index 774bd27cc..3d1d79977 100644
--- a/data/storage/mysql/create_costdetails_tables.sql
+++ b/data/storage/mysql/create_costdetails_tables.sql
@@ -19,7 +19,8 @@ CREATE TABLE `cost_details` (
`timespans` text,
`source` varchar(64) NOT NULL,
`runid` varchar(64) NOT NULL,
+ `cost_time` datetime NOT NULL,
PRIMARY KEY (`id`),
- UNIQUE KEY `costid` (`cgrid`,`subject`,`runid`)
+ UNIQUE KEY `costid` (`cgrid`,`runid`)
);
diff --git a/data/storage/mysql/create_mediator_tables.sql b/data/storage/mysql/create_mediator_tables.sql
index 0b2e017d2..a81718a64 100644
--- a/data/storage/mysql/create_mediator_tables.sql
+++ b/data/storage/mysql/create_mediator_tables.sql
@@ -9,6 +9,7 @@ CREATE TABLE `rated_cdrs` (
`runid` varchar(64) NOT NULL,
`subject` varchar(64) NOT NULL,
`cost` DECIMAL(20,4) DEFAULT NULL,
+ `mediation_time` datetime NOT NULL,
`extra_info` text,
PRIMARY KEY (`id`),
UNIQUE KEY `costid` (`cgrid`,`runid`)
diff --git a/docs/cgrates_cfg.rst b/docs/cgrates_cfg.rst
new file mode 100644
index 000000000..4e3562d46
--- /dev/null
+++ b/docs/cgrates_cfg.rst
@@ -0,0 +1,122 @@
+cgr-engine configuration file
+=============================
+
+Organized into configuration sections. All configuration options come with defaults and we have tried our best to choose the best ones for a minimum of efforts necessary when running.
+
+Bellow is the default configuration file which comes hardcoded into cgr-engine, most of them being explained and exemplified there.
+
+::
+
+ [global]
+ # ratingdb_type = redis # Rating subsystem database: .
+ # ratingdb_host = 127.0.0.1 # Rating subsystem database host address.
+ # ratingdb_port = 6379 # Rating subsystem port to reach the database.
+ # ratingdb_name = 10 # Rating subsystem database name to connect to.
+ # ratingdb_user = # Rating subsystem username to use when connecting to database.
+ # ratingdb_passwd = # Rating subsystem password to use when connecting to database.
+ # accountdb_type = redis # Accounting subsystem database: .
+ # accountdb_host = 127.0.0.1 # Accounting subsystem database host address.
+ # accountdb_port = 6379 # Accounting subsystem port to reach the database.
+ # accountdb_name = 11 # Accounting subsystem database name to connect to.
+ # accountdb_user = # Accounting subsystem username to use when connecting to database.
+ # accountdb_passwd = # Accounting subsystem password to use when connecting to database.
+ # stordb_type = mysql # Stor database type to use:
+ # stordb_host = 127.0.0.1 # The host to connect to. Values that start with / are for UNIX domain sockets.
+ # stordb_port = 3306 # The port to reach the logdb.
+ # stordb_name = cgrates # The name of the log database to connect to.
+ # stordb_user = cgrates # Username to use when connecting to stordb.
+ # stordb_passwd = CGRateS.org # Password to use when connecting to stordb.
+ # dbdata_encoding = msgpack # The encoding used to store object data in strings:
+ # rpc_json_listen = 127.0.0.1:2012 # RPC JSON listening address
+ # rpc_gob_listen = 127.0.0.1:2013 # RPC GOB listening address
+ # http_listen = 127.0.0.1:2080 # HTTP listening address
+ # default_reqtype = rated # Default request type to consider when missing from requests: <""|prepaid|postpaid|pseudoprepaid|rated>.
+ # default_tor = call # Default Type of Record to consider when missing from requests.
+ # default_tenant = cgrates.org # Default Tenant to consider when missing from requests.
+ # default_subject = cgrates # Default rating Subject to consider when missing from requests.
+ # rounding_method = *middle # Rounding method for floats/costs: <*up|*middle|*down>
+ # rounding_decimals = 4 # Number of decimals to round float/costs at
+
+ [balancer]
+ # enabled = false # Start Balancer service: .
+
+ [rater]
+ # enabled = false # Enable RaterCDRSExportPath service: .
+ # balancer = # Register to Balancer as worker: <""|internal|127.0.0.1:2013>.
+
+ [scheduler]
+ # enabled = false # Starts Scheduler service: .
+
+ [cdrs]
+ # enabled = false # Start the CDR Server service: .
+ # extra_fields = # Extra fields to store in CDRs
+ # mediator = # Address where to reach the Mediator. Empty for disabling mediation. <""|internal>
+
+ [cdre]
+ # cdr_format = csv # Exported CDRs format
+ # extra_fields = # List of extra fields to be exported out in CDRs
+ # export_dir = /var/log/cgrates/cdr/cdrexport/csv # Path where the exported CDRs will be placed
+
+ [cdrc]
+ # enabled = false # Enable CDR client functionality
+ # cdrs = internal # Address where to reach CDR server.
+ # cdrs_method = http_cgr # Mechanism to use when posting CDRs on server
+ # run_delay = 0 # Sleep interval in seconds between consecutive runs, 0 to use automation via inotify
+ # cdr_type = csv # CDR file format .
+ # cdr_in_dir = /var/log/cgrates/cdr/cdrc/in # Absolute path towards the directory where the CDRs are stored.
+ # cdr_out_dir = /var/log/cgrates/cdr/cdrc/out # Absolute path towards the directory where processed CDRs will be moved.
+ # cdr_source_id = freeswitch_csv # Free form field, tag identifying the source of the CDRs within CGRS database.
+ # accid_field = 0 # Accounting id field identifier. Use index number in case of .csv cdrs.
+ # reqtype_field = 1 # Request type field identifier. Use index number in case of .csv cdrs.
+ # direction_field = 2 # Direction field identifier. Use index numbers in case of .csv cdrs.
+ # tenant_field = 3 # Tenant field identifier. Use index numbers in case of .csv cdrs.
+ # tor_field = 4 # Type of Record field identifier. Use index numbers in case of .csv cdrs.
+ # account_field = 5 # Account field identifier. Use index numbers in case of .csv cdrs.
+ # subject_field = 6 # Subject field identifier. Use index numbers in case of .csv CDRs.
+ # destination_field = 7 # Destination field identifier. Use index numbers in case of .csv cdrs.
+ # answer_time_field = 8 # Answer time field identifier. Use index numbers in case of .csv cdrs.
+ # duration_field = 9 # Duration field identifier. Use index numbers in case of .csv cdrs.
+ # extra_fields = # Extra fields identifiers. For .csv, format: :[...,:]
+
+ [mediator]
+ # enabled = false # Starts Mediator service: .
+ # rater = internal # Address where to reach the Rater:
+ # rater_reconnects = 3 # Number of reconnects to rater before giving up.
+ # run_ids = # Identifiers of each extra mediation to run on CDRs
+ # reqtype_fields = # Name of request type fields to be used during extra mediation. Use index number in case of .csv cdrs.
+ # direction_fields = # Name of direction fields to be used during extra mediation. Use index numbers in case of .csv cdrs.
+ # tenant_fields = # Name of tenant fields to be used during extra mediation. Use index numbers in case of .csv cdrs.
+ # tor_fields = # Name of tor fields to be used during extra mediation. Use index numbers in case of .csv cdrs.
+ # account_fields = # Name of account fields to be used during extra mediation. Use index numbers in case of .csv cdrs.
+ # subject_fields = # Name of fields to be used during extra mediation. Use index numbers in case of .csv cdrs.
+ # destination_fields = # Name of destination fields to be used during extra mediation. Use index numbers in case of .csv cdrs.
+ # answer_time_fields = # Name of time_answer fields to be used during extra mediation. Use index numbers in case of .csv cdrs.
+ # duration_fields = # Name of duration fields to be used during extra mediation. Use index numbers in case of .csv cdrs.
+
+ [session_manager]
+ # enabled = false # Starts SessionManager service: .
+ # switch_type = freeswitch # Defines the type of switch behind: .
+ # rater = internal # Address where to reach the Rater.
+ # rater_reconnects = 3 # Number of reconnects to rater before giving up.
+ # debit_interval = 10 # Interval to perform debits on.
+ # max_call_duration = 3h # Maximum call duration a prepaid call can last
+
+ [freeswitch]
+ # server = 127.0.0.1:8021 # Adress where to connect to FreeSWITCH socket.
+ # passwd = ClueCon # FreeSWITCH socket password.
+ # reconnects = 5 # Number of attempts on connect failure.
+
+ [history_server]
+ # enabled = false # Starts History service: .
+ # history_dir = /var/log/cgrates/history # Location on disk where to store history files.
+ # save_interval = 1s # Interval to save changed cache into .git archive
+
+ [history_agent]
+ # enabled = false # Starts History as a client: .
+ # server = internal # Address where to reach the master history server:
+
+ [mailer]
+ # server = localhost # The server to use when sending emails out
+ # auth_user = cgrates # Authenticate to email server using this user
+ # auth_passwd = CGRateS.org # Authenticate to email server with this password
+ # from_address = cgr-mailer@localhost.localdomain # From address used when sending emails out
diff --git a/docs/configuration.rst b/docs/configuration.rst
index d7ff7cf5d..fa9159c3c 100644
--- a/docs/configuration.rst
+++ b/docs/configuration.rst
@@ -1,366 +1,16 @@
-4. Configuration
-================
+Configuration
+=============
+
+The behaviour of **CGRateS** can be externally influenced by following means:
+
+- Engine configuration file, ussually located at */etc/cgrates/cgrates.cfg*
+- Tariff Plans: set of files used to import customer rating and accounting data into CGRateS.
+- RPC APIs: set of JSON/GOB encoded APIs remotely available for various operational/administrative tasks.
+
+.. toctree::
+ :maxdepth: 2
+
+ cgrates_cfg
+ tariff_plans
-4.1. cgr-engine configuration file
----------------------------------
-Organized into configuration sections. All configuration options come with defaults and we have tried our best to choose the best ones for a minimum of efforts necessary when running.
-
-Bellow is the default configuration file which comes hardcoded into cgr-engine, most of them being explained and exemplified there.
-
-::
-
- [global]
- # ratingdb_type = redis # Rating subsystem database: .
- # ratingdb_host = 127.0.0.1 # Rating subsystem database host address.
- # ratingdb_port = 6379 # Rating subsystem port to reach the database.
- # ratingdb_name = 10 # Rating subsystem database name to connect to.
- # ratingdb_user = # Rating subsystem username to use when connecting to database.
- # ratingdb_passwd = # Rating subsystem password to use when connecting to database.
- # accountdb_type = redis # Accounting subsystem database: .
- # accountdb_host = 127.0.0.1 # Accounting subsystem database host address.
- # accountdb_port = 6379 # Accounting subsystem port to reach the database.
- # accountdb_name = 11 # Accounting subsystem database name to connect to.
- # accountdb_user = # Accounting subsystem username to use when connecting to database.
- # accountdb_passwd = # Accounting subsystem password to use when connecting to database.
- # stordb_type = mysql # Stor database type to use:
- # stordb_host = 127.0.0.1 # The host to connect to. Values that start with / are for UNIX domain sockets.
- # stordb_port = 3306 # The port to reach the logdb.
- # stordb_name = cgrates # The name of the log database to connect to.
- # stordb_user = cgrates # Username to use when connecting to stordb.
- # stordb_passwd = CGRateS.org # Password to use when connecting to stordb.
- # dbdata_encoding = msgpack # The encoding used to store object data in strings:
- # rpc_encoding = json # RPC encoding used on APIs: .
- # default_reqtype = rated # Default request type to consider when missing from requests: <""|prepaid|postpaid|pseudoprepaid|rated>.
- # default_tor = 0 # Default Type of Record to consider when missing from requests.
- # default_tenant = 0 # Default Tenant to consider when missing from requests.
- # default_subject = 0 # Default rating Subject to consider when missing from requests.
- # rounding_method = *middle # Rounding method for floats/costs: <*up|*middle|*down>
- # rounding_decimals = 4 # Number of decimals to round float/costs at
-
- [balancer]
- # enabled = false # Start Balancer service: .
- # listen = 127.0.0.1:2012 # Balancer listen interface: <""|x.y.z.y:1234>.
-
- [rater]
- # enabled = false # Enable RaterCDRSExportPath service: .
- # balancer = disabled # Register to Balancer as worker: .
- # listen = 127.0.0.1:2012 # Rater's listening interface: .
-
- [scheduler]
- # enabled = false # Starts Scheduler service: .
-
- [cdrs]
- # enabled = false # Start the CDR Server service: .
- # listen=127.0.0.1:2022 # CDRS's listening interface: .
- # extra_fields = # Extra fields to store in CDRs
- # mediator = # Address where to reach the Mediator. Empty for disabling mediation. <""|internal>
-
- [cdre]
- # cdr_format = csv # Exported CDRs format
- # extra_fields = # List of extra fields to be exported out in CDRs
- # export_dir = /var/log/cgrates/cdr/out/cgr # Path where the exported CDRs will be placed
-
- [cdrc]
- # enabled = false # Enable CDR client functionality
- # cdrs = 127.0.0.1:2022 # Address where to reach CDR server
- # cdrs_method = http_cgr # Mechanism to use when posting CDRs on server
- # run_delay = 0 # Sleep interval in seconds between consecutive runs, 0 to use automation via inotify
- # cdr_type = csv # CDR file format .
- # cdr_in_dir = /var/log/cgrates/cdr/in/csv # Absolute path towards the directory where the CDRs are stored.
- # cdr_out_dir = /var/log/cgrates/cdr/out/csv # Absolute path towards the directory where processed CDRs will be moved.
- # cdr_source_id = freeswitch_csv # Free form field, tag identifying the source of the CDRs within CGRS database.
- # accid_field = 0 # Accounting id field identifier. Use index number in case of .csv cdrs.
- # reqtype_field = 1 # Request type field identifier. Use index number in case of .csv cdrs.
- # direction_field = 2 # Direction field identifier. Use index numbers in case of .csv cdrs.
- # tenant_field = 3 # Tenant field identifier. Use index numbers in case of .csv cdrs.
- # tor_field = 4 # Type of Record field identifier. Use index numbers in case of .csv cdrs.
- # account_field = 5 # Account field identifier. Use index numbers in case of .csv cdrs.
- # subject_field = 6 # Subject field identifier. Use index numbers in case of .csv CDRs.
- # destination_field = 7 # Destination field identifier. Use index numbers in case of .csv cdrs.
- # answer_time_field = 8 # Answer time field identifier. Use index numbers in case of .csv cdrs.
- # duration_field = 9 # Duration field identifier. Use index numbers in case of .csv cdrs.
- # extra_fields = 10:supplier,11:orig_ip # Extra fields identifiers. For .csv, format: :[,:]
-
- [mediator]
- # enabled = false # Starts Mediator service: .
- # listen=internal # Mediator's listening interface: .
- # rater = 127.0.0.1:2012 # Address where to reach the Rater:
- # rater_reconnects = 3 # Number of reconnects to rater before giving up.
- # run_ids = # Identifiers of each extra mediation to run on CDRs
- # reqtype_fields = # Name of request type fields to be used during extra mediation. Use index number in case of .csv cdrs.
- # direction_fields = # Name of direction fields to be used during extra mediation. Use index numbers in case of .csv cdrs.
- # tenant_fields = # Name of tenant fields to be used during extra mediation. Use index numbers in case of .csv cdrs.
- # tor_fields = # Name of tor fields to be used during extra mediation. Use index numbers in case of .csv cdrs.
- # account_fields = # Name of account fields to be used during extra mediation. Use index numbers in case of .csv cdrs.
- # subject_fields = # Name of fields to be used during extra mediation. Use index numbers in case of .csv cdrs.
- # destination_fields = # Name of destination fields to be used during extra mediation. Use index numbers in case of .csv cdrs.
- # answer_time_fields = # Name of time_answer fields to be used during extra mediation. Use index numbers in case of .csv cdrs.
- # duration_fields = # Name of duration fields to be used during extra mediation. Use index numbers in case of .csv cdrs.
-
- [session_manager]
- # enabled = false # Starts SessionManager service: .
- # switch_type = freeswitch # Defines the type of switch behind: .
- # rater = 127.0.0.1:2012 # Address where to reach the Rater.
- # rater_reconnects = 3 # Number of reconnects to rater before giving up.
- # debit_interval = 5 # Interval to perform debits on.
-
- [freeswitch]
- # server = 127.0.0.1:8021 # Adress where to connect to FreeSWITCH socket.
- # passwd = ClueCon # FreeSWITCH socket password.
- # reconnects = 5 # Number of attempts on connect failure.
-
- [history_server]
- # enabled = false # Starts History service: .
- # listen = 127.0.0.1:2013 # Listening addres for history server:
- # history_dir = /var/log/cgrates/history # Location on disk where to store history files.
- # save_interval = 1s # Interval to save changed cache into .git archive
-
- [history_agent]
- # enabled = false # Starts History as a client: .
- # server = 127.0.0.1:2013 # Address where to reach the master history server:
-
-
-4.2. Tariff plans
------------------
-
-For importing the data into CGRateS database we are using cvs files. The import process can be started as many times it is desired with one ore more csv files and the existing values are overwritten. If the -flush option is used then the database is cleaned before importing.For more details see the cgr-loader tool from the tutorial chapter.
-
-The rest of this section we will describe the content of every csv files.
-
-4.2.1. Rates profile
-~~~~~~~~~~~~~~~~~~~~
-
-The rates profile describes the prices to be applied for various calls to various destinations in various time frames. When a call is made the CGRateS system will locate the rates to be applied to the call using the rating profiles.
-
-+------------+-----+-----------+-------------+----------------------+----------------+----------------------+
-| Tenant | TOR | Direction | Subject | RatesFallbackSubject | RatesTimingTag | ActivationTime |
-+============+=====+===========+=============+======================+================+======================+
-| CUSTOMER_1 | 0 | OUT | rif:from:tm | danb | PREMIUM | 2012-01-01T00:00:00Z |
-+------------+-----+-----------+-------------+----------------------+----------------+----------------------+
-| CUSTOMER_1 | 0 | OUT | rif:from:tm | danb | STANDARD | 2012-02-28T00:00:00Z |
-+------------+-----+-----------+-------------+----------------------+----------------+----------------------+
-
-Tenant
- Used to distinguish between carriers if more than one share the same database in the CGRates system.
-TOR
- Type of record specifies the kind of transmission this rate profile applies to.
-Direction
- Can be IN or OUT for the INBOUND and OUTBOUND calls.
-Subject
- The client/user for who this profile is detailing the rates.
-RatesFallbackSubject
- This specifies another profile to be used in case the call destination will not be found in the current profile. The same tenant, tor and direction will be used.
-RatesTimingTag
- Forwards to a tag described in the rates timing file to be used for this profile.
-ActivationTime
- Multiple rates timings/prices can be created for one profile with different activation times. When a call is made the appropriate profile(s) will be used to rate the call. So future prices can be defined here and the activation time can be set as appropriate.
-
-4.2.2. Rates timing
-~~~~~~~~~~~~~~~~~~~
-
-This file makes links between a ratings and timings so each of them can be described once and various combinations are made possible.
-
-+----------+----------------+--------------+--------+
-| Tag | RatesTag | TimingTag | Weight |
-+==========+================+==============+========+
-| STANDARD | RT_STANDARD | WORKDAYS_00 | 10 |
-+----------+----------------+--------------+--------+
-| STANDARD | RT_STD_WEEKEND | WORKDAYS_18 | 10 |
-+----------+----------------+--------------+--------+
-
-Tag
- A string by witch this rates timing will be referenced in other places by.
-RatesTag
- The rating tag described in the rates file.
-TimingTag
- The timing tag described in the timing file
-Weight
- If multiple timings cab be applied to a call the one with the lower weight wins. An example here can be the Christmas day: we can have a special timing for this day but the regular day of the week timing can also be applied to this day. The weight will differentiate between the two timings.
-
-
-4.2.3. Rates
-~~~~~~~~~~~~
-Defines price groups for various destinations which will be associated to various timings.
-
-+---------------------+-----------------+------------+-------+-------------+
-| Tag | DestinationsTag | ConnectFee | Price | BillingUnit |
-+=====================+=================+============+=======+=============+
-| RT_STANDARD | GERMANY | 0 | 0.2 | 1 |
-+---------------------+-----------------+------------+-------+-------------+
-| RT_STANDARD | GERMANY_O2 | 0 | 0.1 | 1 |
-+---------------------+-----------------+------------+-------+-------------+
-
-
-Tag
- A string by witch this rate will be referenced in other places by.
-DestinationsTag
- The destination tag witch these rates apply to.
-ConnectFee
- The price to be charged once at the beginning of the call to the specified destination.
-Price
- The price for the billing unit expressed in cents.
-BillingUnit
- The billing unit expressed in seconds
-
-4.2.4. Timings
-~~~~~~~~~~~~~~
-Describes the time periods that have different rates attached to them.
-
-+-----------------+--------+-----------+-----------+----------+
-| Tag | Months | MonthDays | WeekDays | StartTime|
-+=================+========+===========+===========+==========+
-| WORKDAYS | \*all | \*all | 1;2;3;4;5 | 00:00:00 |
-+-----------------+--------+-----------+-----------+----------+
-| WEEKENDS | \*all | \*all | 6,7 | 00:00:00 |
-+-----------------+--------+-----------+-----------+----------+
-| DAILY_SAME_TIME | \*all | \*all | \*all | \*now |
-+-----------------+--------+-----------+-----------+----------+
-| ONE_TIME_RUN | \*none | \*none | \*none | \*now |
-+-----------------+--------+-----------+-----------+----------+
-
-Tag
- A string by witch this timing will be referenced in other places by.
-Months
- Integers from 1=January to 12=December separated by semicolons (;) specifying the months for this time period.
-MonthDays
- Integers from 1 to 31 separated by semicolons (;) specifying the month days for this time period.
-WeekDays
- Integers from 1=Monday to 7=Sunday separated by semicolons (;) specifying the week days for this time period.
-StartTime
- The start time for this time period. \*now will be replaced with the time of the data importing.
-
-4.2.5. Destinations
-~~~~~~~~~~~~~~~~~~~
-
-The destinations are binding together various prefixes / caller ids to define a logical destination group. A prefix can appear in multiple destination groups.
-
-+------------+--------+
-| Tag | Prefix |
-+============+========+
-| GERMANY | 49 |
-+------------+--------+
-| GERMANY_O2 | 49176 |
-+------------+--------+
-
-Tag
- A string by witch this destination will be referenced in other places by.
-Prefix
- The prefix or caller id to be added to the specified destination.
-
-4.2.6. Account actions
-~~~~~~~~~~~~~~~~~~~~~~
-
-Describes the actions to be applied to the clients/users accounts. There are two kinds of actions: timed and triggered. For the timed actions there is a scheduler application that reads them from the database and executes them at the appropriate timings. The triggered actions are executed when the specified balance counters reach certain thresholds.
-
-The accounts hold the various balances and counters to activate the triggered actions for each the client.
-
-Balance types are: MONETARY, SMS, INTERNET, INTERNET_TIME, MINUTES.
-
-+------------+---------+-----------+------------------+------------------+
-|Tenant | Account | Direction | ActionTimingsTag | ActionTriggersTag|
-+============+=========+===========+==================+==================+
-| CUSTOMER_1 | rif | OUT | STANDARD_ABO | STANDARD_TRIGGER |
-+------------+---------+-----------+------------------+------------------+
-| CUSTOMER_1 | dan | OUT | STANDARD_ABO | STANDARD_TRIGGER |
-+------------+---------+-----------+------------------+------------------+
-
-Tenant
- Used to distinguish between carriers if more than one share the same database in the CGRates system.
-Account
- The identifier for the user's account.
-Direction
- Can be IN or OUT for the INBOUND and OUTBOUND calls.
-ActionTimingsTag
- Forwards to a timed action group that will be used on this account.
-ActionTriggersTag
- Forwards to a triggered action group that will be applied to this account.
-
-Action triggers
-~~~~~~~~~~~~~~
-For each account there are counters that record the activity on various balances. Action triggers allow when a counter reaches a threshold to activate a group of actions. After the execution the action trigger is marked as used and will no longer be evaluated until the triggers are reset. See actions for action trigger resetting.
-
-+------------------+------------+----------------+----------------+------------+--------+
-| Tag | BalanceTag | ThresholdValue | DestinationTag | ActionsTag | Weight |
-+==================+============+================+================+============+========+
-| STANDARD_TRIGGER | MONETARY | 30 | \*all | SOME_1 | 10 |
-+------------------+------------+----------------+----------------+------------+--------+
-| STANDARD_TRIGGER | SMS | 30 | \*all | SOME_2 | 10 |
-+------------------+------------+----------------+----------------+------------+--------+
-
-Tag
- A string by witch this action trigger will be referenced in other places by.
-BalanceTag
- Specifies the balance counter by which this action will be triggered. Can be MONETARY, SMS, INTERNET, INTERNET_TIME, MINUTES.
-ThresholdValue
- The value of the balance counter that will trigger this action.
-DestinationTag
- This field is used only if the balanceTag is MINUTES. If the balance counter monitors call minutes this field indicates the destination of the calls for which the minutes are recorded.
-ActionsTag
- Forwards to an action group to be executed when the threshold is reached.
-Weight
- Specifies the order for these triggers to be evaluated. If there are multiple triggers are fired in the same time the ones with the lower weight will be executed first.
-
-4.2.7. Action timings
-~~~~~~~~~~~~~~~~~~~~~
-
-+--------------+------------+------------------+--------+
-| Tag | ActionsTag | TimingTag | Weight |
-+==============+============+==================+========+
-| STANDARD_ABO | SOME | WEEKLY_SAME_TIME | 10 |
-+--------------+------------+------------------+--------+
-| STANDARD_ABO | SOME | WEEKLY_SAME_TIME | 10 |
-+--------------+------------+------------------+--------+
-
-Tag
- A string by witch this action timing will be referenced in other places by.
-ActionsTag
- Forwards to an action group to be executed when the timing is right.
-TimingTag
- A timing (one time or recurrent) at which the action group will be executed
-Weight
- Specifies the order for these timings to be evaluated. If there are multiple action timings set to be execute on the same time the ones with the lower weight will be executed first.
-
-4.2.8. Actions
-~~~~~~~~~~~~~~
-
-+--------+-------------+------------+-------+----------------+-----------+------------+---------------+--------+
-| Tag | Action | BalanceTag | Units | DestinationTag | PriceType | PriceValue | MinutesWeight | Weight |
-+========+=============+============+=======+================+===========+============+===============+========+
-| SOME | TOPUP_RESET | MONETARY | 10 | \*all | | | | 10 |
-+--------+-------------+------------+-------+----------------+-----------+------------+---------------+--------+
-| SOME_1 | DEBIT | MINUTES | 10 | GERMANY_O2 | PERCENT | 25 | 10 | 10 |
-+--------+-------------+------------+-------+----------------+-----------+------------+---------------+--------+
-
-Tag
- A string by witch this action will be referenced in other places by.
-Action
- The action type. Can have one of the following:
-
- + LOG: Logs the other action values (for debugging purposes).
- + RESET_TRIGGERS: Marks all action triggers as ready to be executed.
- + SET_POSTPAID: Sets account to postpaid, maintains it's balances.
- + RESET_POSTPAID: Set account to postpaid, reset all it's balances.
- + SET_PREPAID: Sets account to prepaid, maintains it's balances. Makes sense after an account was set to POSTPAID and admin wants it back.
- + RESET_PREPAID: Set account to prepaid, reset all it's balances.
- + TOPUP_RESET: Add account balance. If previous balance found of the same type, reset it before adding.
- + TOPUP: Add account balance. If the specific balance is not defined, define it (eg: minutes per destination).
- + DEBIT: Debit account balance.
- + RESET_COUNTER: Sets the counter for the BalanceTag to 0
- + RESET_ALL_COUNTERS: Sets all counters to 0
-
-BalanceTag
- The balance on which the action will operate
-Units
- The units which will be operated on the balance BalanceTag.
-DestinationTag
- This field is used only if the balanceTag is MINUTES. Specifies the destination of the minutes to be operated.
-PriceType
- This field is used only if the balanceTag is MINUTES. Specifies if the minutes price will be absolute or a percent of the normal price, Can be ABSOLUTE or PERCENT. If the value is percent the
-PriceValue
- This field is used only if the balanceTag is MINUTES. The price for each second.
-MinutesWeight
- This field is used only if the balanceTag is MINUTES. If more minute balances are suitable for a call the one with smaller weight will be used first.
-Weight
- If there are multiple actions in a group, they will be executed in the order of their weight (smaller first).
-
diff --git a/docs/tariff_plans.rst b/docs/tariff_plans.rst
new file mode 100644
index 000000000..7dd43b7ee
--- /dev/null
+++ b/docs/tariff_plans.rst
@@ -0,0 +1,242 @@
+Tariff Plans
+============
+
+For importing the data into CGRateS database we are using cvs files. The import process can be started as many times it is desired with one ore more csv files and the existing values are overwritten. If the -flush option is used then the database is cleaned before importing.For more details see the cgr-loader tool from the tutorial chapter.
+
+The rest of this section we will describe the content of every csv files.
+
+4.2.1. Rates profile
+~~~~~~~~~~~~~~~~~~~~
+
+The rates profile describes the prices to be applied for various calls to various destinations in various time frames. When a call is made the CGRateS system will locate the rates to be applied to the call using the rating profiles.
+
++------------+-----+-----------+-------------+----------------------+----------------+----------------------+
+| Tenant | TOR | Direction | Subject | RatesFallbackSubject | RatesTimingTag | ActivationTime |
++============+=====+===========+=============+======================+================+======================+
+| CUSTOMER_1 | 0 | OUT | rif:from:tm | danb | PREMIUM | 2012-01-01T00:00:00Z |
++------------+-----+-----------+-------------+----------------------+----------------+----------------------+
+| CUSTOMER_1 | 0 | OUT | rif:from:tm | danb | STANDARD | 2012-02-28T00:00:00Z |
++------------+-----+-----------+-------------+----------------------+----------------+----------------------+
+
+Tenant
+ Used to distinguish between carriers if more than one share the same database in the CGRates system.
+TOR
+ Type of record specifies the kind of transmission this rate profile applies to.
+Direction
+ Can be IN or OUT for the INBOUND and OUTBOUND calls.
+Subject
+ The client/user for who this profile is detailing the rates.
+RatesFallbackSubject
+ This specifies another profile to be used in case the call destination will not be found in the current profile. The same tenant, tor and direction will be used.
+RatesTimingTag
+ Forwards to a tag described in the rates timing file to be used for this profile.
+ActivationTime
+ Multiple rates timings/prices can be created for one profile with different activation times. When a call is made the appropriate profile(s) will be used to rate the call. So future prices can be defined here and the activation time can be set as appropriate.
+
+4.2.2. Rates timing
+~~~~~~~~~~~~~~~~~~~
+
+This file makes links between a ratings and timings so each of them can be described once and various combinations are made possible.
+
++----------+----------------+--------------+--------+
+| Tag | RatesTag | TimingTag | Weight |
++==========+================+==============+========+
+| STANDARD | RT_STANDARD | WORKDAYS_00 | 10 |
++----------+----------------+--------------+--------+
+| STANDARD | RT_STD_WEEKEND | WORKDAYS_18 | 10 |
++----------+----------------+--------------+--------+
+
+Tag
+ A string by witch this rates timing will be referenced in other places by.
+RatesTag
+ The rating tag described in the rates file.
+TimingTag
+ The timing tag described in the timing file
+Weight
+ If multiple timings cab be applied to a call the one with the lower weight wins. An example here can be the Christmas day: we can have a special timing for this day but the regular day of the week timing can also be applied to this day. The weight will differentiate between the two timings.
+
+
+4.2.3. Rates
+~~~~~~~~~~~~
+Defines price groups for various destinations which will be associated to various timings.
+
++---------------------+-----------------+------------+-------+-------------+
+| Tag | DestinationsTag | ConnectFee | Price | BillingUnit |
++=====================+=================+============+=======+=============+
+| RT_STANDARD | GERMANY | 0 | 0.2 | 1 |
++---------------------+-----------------+------------+-------+-------------+
+| RT_STANDARD | GERMANY_O2 | 0 | 0.1 | 1 |
++---------------------+-----------------+------------+-------+-------------+
+
+
+Tag
+ A string by witch this rate will be referenced in other places by.
+DestinationsTag
+ The destination tag witch these rates apply to.
+ConnectFee
+ The price to be charged once at the beginning of the call to the specified destination.
+Price
+ The price for the billing unit expressed in cents.
+BillingUnit
+ The billing unit expressed in seconds
+
+4.2.4. Timings
+~~~~~~~~~~~~~~
+Describes the time periods that have different rates attached to them.
+
++-----------------+--------+-----------+-----------+----------+
+| Tag | Months | MonthDays | WeekDays | StartTime|
++=================+========+===========+===========+==========+
+| WORKDAYS | \*all | \*all | 1;2;3;4;5 | 00:00:00 |
++-----------------+--------+-----------+-----------+----------+
+| WEEKENDS | \*all | \*all | 6,7 | 00:00:00 |
++-----------------+--------+-----------+-----------+----------+
+| DAILY_SAME_TIME | \*all | \*all | \*all | \*now |
++-----------------+--------+-----------+-----------+----------+
+| ONE_TIME_RUN | \*none | \*none | \*none | \*now |
++-----------------+--------+-----------+-----------+----------+
+
+Tag
+ A string by witch this timing will be referenced in other places by.
+Months
+ Integers from 1=January to 12=December separated by semicolons (;) specifying the months for this time period.
+MonthDays
+ Integers from 1 to 31 separated by semicolons (;) specifying the month days for this time period.
+WeekDays
+ Integers from 1=Monday to 7=Sunday separated by semicolons (;) specifying the week days for this time period.
+StartTime
+ The start time for this time period. \*now will be replaced with the time of the data importing.
+
+4.2.5. Destinations
+~~~~~~~~~~~~~~~~~~~
+
+The destinations are binding together various prefixes / caller ids to define a logical destination group. A prefix can appear in multiple destination groups.
+
++------------+--------+
+| Tag | Prefix |
++============+========+
+| GERMANY | 49 |
++------------+--------+
+| GERMANY_O2 | 49176 |
++------------+--------+
+
+Tag
+ A string by witch this destination will be referenced in other places by.
+Prefix
+ The prefix or caller id to be added to the specified destination.
+
+4.2.6. Account actions
+~~~~~~~~~~~~~~~~~~~~~~
+
+Describes the actions to be applied to the clients/users accounts. There are two kinds of actions: timed and triggered. For the timed actions there is a scheduler application that reads them from the database and executes them at the appropriate timings. The triggered actions are executed when the specified balance counters reach certain thresholds.
+
+The accounts hold the various balances and counters to activate the triggered actions for each the client.
+
+Balance types are: MONETARY, SMS, INTERNET, INTERNET_TIME, MINUTES.
+
++------------+---------+-----------+------------------+------------------+
+|Tenant | Account | Direction | ActionTimingsTag | ActionTriggersTag|
++============+=========+===========+==================+==================+
+| CUSTOMER_1 | rif | OUT | STANDARD_ABO | STANDARD_TRIGGER |
++------------+---------+-----------+------------------+------------------+
+| CUSTOMER_1 | dan | OUT | STANDARD_ABO | STANDARD_TRIGGER |
++------------+---------+-----------+------------------+------------------+
+
+Tenant
+ Used to distinguish between carriers if more than one share the same database in the CGRates system.
+Account
+ The identifier for the user's account.
+Direction
+ Can be IN or OUT for the INBOUND and OUTBOUND calls.
+ActionTimingsTag
+ Forwards to a timed action group that will be used on this account.
+ActionTriggersTag
+ Forwards to a triggered action group that will be applied to this account.
+
+Action triggers
+~~~~~~~~~~~~~~
+For each account there are counters that record the activity on various balances. Action triggers allow when a counter reaches a threshold to activate a group of actions. After the execution the action trigger is marked as used and will no longer be evaluated until the triggers are reset. See actions for action trigger resetting.
+
++------------------+------------+----------------+----------------+------------+--------+
+| Tag | BalanceTag | ThresholdValue | DestinationTag | ActionsTag | Weight |
++==================+============+================+================+============+========+
+| STANDARD_TRIGGER | MONETARY | 30 | \*all | SOME_1 | 10 |
++------------------+------------+----------------+----------------+------------+--------+
+| STANDARD_TRIGGER | SMS | 30 | \*all | SOME_2 | 10 |
++------------------+------------+----------------+----------------+------------+--------+
+
+Tag
+ A string by witch this action trigger will be referenced in other places by.
+BalanceTag
+ Specifies the balance counter by which this action will be triggered. Can be MONETARY, SMS, INTERNET, INTERNET_TIME, MINUTES.
+ThresholdValue
+ The value of the balance counter that will trigger this action.
+DestinationTag
+ This field is used only if the balanceTag is MINUTES. If the balance counter monitors call minutes this field indicates the destination of the calls for which the minutes are recorded.
+ActionsTag
+ Forwards to an action group to be executed when the threshold is reached.
+Weight
+ Specifies the order for these triggers to be evaluated. If there are multiple triggers are fired in the same time the ones with the lower weight will be executed first.
+
+4.2.7. Action timings
+~~~~~~~~~~~~~~~~~~~~~
+
++--------------+------------+------------------+--------+
+| Tag | ActionsTag | TimingTag | Weight |
++==============+============+==================+========+
+| STANDARD_ABO | SOME | WEEKLY_SAME_TIME | 10 |
++--------------+------------+------------------+--------+
+| STANDARD_ABO | SOME | WEEKLY_SAME_TIME | 10 |
++--------------+------------+------------------+--------+
+
+Tag
+ A string by witch this action timing will be referenced in other places by.
+ActionsTag
+ Forwards to an action group to be executed when the timing is right.
+TimingTag
+ A timing (one time or recurrent) at which the action group will be executed
+Weight
+ Specifies the order for these timings to be evaluated. If there are multiple action timings set to be execute on the same time the ones with the lower weight will be executed first.
+
+4.2.8. Actions
+~~~~~~~~~~~~~~
+
++--------+-------------+------------+-------+----------------+-----------+------------+---------------+--------+
+| Tag | Action | BalanceTag | Units | DestinationTag | PriceType | PriceValue | MinutesWeight | Weight |
++========+=============+============+=======+================+===========+============+===============+========+
+| SOME | TOPUP_RESET | MONETARY | 10 | \*all | | | | 10 |
++--------+-------------+------------+-------+----------------+-----------+------------+---------------+--------+
+| SOME_1 | DEBIT | MINUTES | 10 | GERMANY_O2 | PERCENT | 25 | 10 | 10 |
++--------+-------------+------------+-------+----------------+-----------+------------+---------------+--------+
+
+Tag
+ A string by witch this action will be referenced in other places by.
+Action
+ The action type. Can have one of the following:
+
+ + LOG: Logs the other action values (for debugging purposes).
+ + RESET_TRIGGERS: Marks all action triggers as ready to be executed.
+ + SET_POSTPAID: Sets account to postpaid, maintains it's balances.
+ + RESET_POSTPAID: Set account to postpaid, reset all it's balances.
+ + SET_PREPAID: Sets account to prepaid, maintains it's balances. Makes sense after an account was set to POSTPAID and admin wants it back.
+ + RESET_PREPAID: Set account to prepaid, reset all it's balances.
+ + TOPUP_RESET: Add account balance. If previous balance found of the same type, reset it before adding.
+ + TOPUP: Add account balance. If the specific balance is not defined, define it (eg: minutes per destination).
+ + DEBIT: Debit account balance.
+ + RESET_COUNTER: Sets the counter for the BalanceTag to 0
+ + RESET_ALL_COUNTERS: Sets all counters to 0
+
+BalanceTag
+ The balance on which the action will operate
+Units
+ The units which will be operated on the balance BalanceTag.
+DestinationTag
+ This field is used only if the balanceTag is MINUTES. Specifies the destination of the minutes to be operated.
+PriceType
+ This field is used only if the balanceTag is MINUTES. Specifies if the minutes price will be absolute or a percent of the normal price, Can be ABSOLUTE or PERCENT. If the value is percent the
+PriceValue
+ This field is used only if the balanceTag is MINUTES. The price for each second.
+MinutesWeight
+ This field is used only if the balanceTag is MINUTES. If more minute balances are suitable for a call the one with smaller weight will be used first.
+Weight
+ If there are multiple actions in a group, they will be executed in the order of their weight (smaller first).
diff --git a/engine/storage_interface.go b/engine/storage_interface.go
index 3262e46f3..e2ddc0558 100644
--- a/engine/storage_interface.go
+++ b/engine/storage_interface.go
@@ -94,9 +94,9 @@ type AccountingStorage interface {
type CdrStorage interface {
Storage
SetCdr(utils.RawCDR) error
- SetRatedCdr(*utils.RatedCDR, string) error
- GetRatedCdrs(time.Time, time.Time) ([]*utils.RatedCDR, error)
- RemRatedCdrs([]string) error
+ SetRatedCdr(*utils.StoredCdr, string) error
+ GetStoredCdrs(time.Time, time.Time, bool, bool) ([]*utils.StoredCdr, error)
+ RemStoredCdrs([]string) error
}
type LogStorage interface {
diff --git a/engine/storage_sql.go b/engine/storage_sql.go
index 1e7f4ae9a..3ebe86656 100644
--- a/engine/storage_sql.go
+++ b/engine/storage_sql.go
@@ -646,7 +646,7 @@ func (self *SQLStorage) LogCallCost(uuid, source, runid string, cc *CallCost) (e
if err != nil {
Logger.Err(fmt.Sprintf("Error marshalling timespans to json: %v", err))
}
- _, err = self.Db.Exec(fmt.Sprintf("INSERT INTO %s (cgrid, accid, direction, tenant, tor, account, subject, destination, connect_fee, cost, timespans, source, runid)VALUES ('%s', '%s','%s', '%s', '%s', '%s', '%s', '%s', %f, %f, '%s','%s','%s')",
+ _, err = self.Db.Exec(fmt.Sprintf("INSERT INTO %s (cgrid, accid, direction, tenant, tor, account, subject, destination, connect_fee, cost, timespans, source, runid, cost_time)VALUES ('%s', '%s','%s', '%s', '%s', '%s', '%s', '%s', %f, %f, '%s','%s','%s',now()) ON DUPLICATE KEY UPDATE direction=values(direction), tenant=values(tenant), tor=values(tor), account=values(account), subject=values(subject), destination=values(destination), connect_fee=values(connect_fee), cost=values(cost), timespans=values(timespans), source=values(source), cost_time=now()",
utils.TBL_COST_DETAILS,
utils.FSCgrId(uuid),
uuid,
@@ -726,14 +726,13 @@ func (self *SQLStorage) SetCdr(cdr utils.RawCDR) (err error) {
return
}
-func (self *SQLStorage) SetRatedCdr(ratedCdr *utils.RatedCDR, extraInfo string) (err error) {
- // ToDo: Add here source and subject
- _, err = self.Db.Exec(fmt.Sprintf("INSERT INTO %s (cgrid,runid,subject,cost,extra_info) VALUES ('%s','%s','%s',%f,'%s')",
+func (self *SQLStorage) SetRatedCdr(storedCdr *utils.StoredCdr, extraInfo string) (err error) {
+ _, err = self.Db.Exec(fmt.Sprintf("INSERT INTO %s (cgrid,runid,subject,cost,mediation_time,extra_info) VALUES ('%s','%s','%s',%f,now(),'%s') ON DUPLICATE KEY UPDATE subject=values(subject),cost=values(cost),extra_info=values(extra_info)",
utils.TBL_RATED_CDRS,
- ratedCdr.CgrId,
- ratedCdr.MediationRunId,
- ratedCdr.Subject,
- ratedCdr.Cost,
+ storedCdr.CgrId,
+ storedCdr.MediationRunId,
+ storedCdr.Subject,
+ storedCdr.Cost,
extraInfo))
if err != nil {
Logger.Err(fmt.Sprintf("failed to execute cdr insert statement: %s", err.Error()))
@@ -741,16 +740,37 @@ func (self *SQLStorage) SetRatedCdr(ratedCdr *utils.RatedCDR, extraInfo string)
return
}
-// Return a slice of rated CDRs from storDb using optional timeStart and timeEnd as filters.
-func (self *SQLStorage) GetRatedCdrs(timeStart, timeEnd time.Time) ([]*utils.RatedCDR, error) {
- var cdrs []*utils.RatedCDR
+// Return a slice of CDRs from storDb using optional filters.
+func (self *SQLStorage) GetStoredCdrs(timeStart, timeEnd time.Time, ignoreErr, ignoreRated bool) ([]*utils.StoredCdr, error) {
+ var cdrs []*utils.StoredCdr
q := fmt.Sprintf("SELECT %s.cgrid,accid,cdrhost,cdrsource,reqtype,direction,tenant,tor,account,%s.subject,destination,answer_time,duration,extra_fields,runid,cost FROM %s LEFT JOIN %s ON %s.cgrid=%s.cgrid LEFT JOIN %s ON %s.cgrid=%s.cgrid", utils.TBL_CDRS_PRIMARY, utils.TBL_CDRS_PRIMARY, utils.TBL_CDRS_PRIMARY, utils.TBL_CDRS_EXTRA, utils.TBL_CDRS_PRIMARY, utils.TBL_CDRS_EXTRA, utils.TBL_RATED_CDRS, utils.TBL_CDRS_PRIMARY, utils.TBL_RATED_CDRS)
- if !timeStart.IsZero() && !timeEnd.IsZero() {
- q += fmt.Sprintf(" WHERE answer_time>='%s' AND answer_time<'%s'", timeStart, timeEnd)
- } else if !timeStart.IsZero() {
- q += fmt.Sprintf(" WHERE answer_time>='%d'", timeStart)
- } else if !timeEnd.IsZero() {
- q += fmt.Sprintf(" WHERE answer_time<'%d'", timeEnd)
+ fltr := ""
+ if !timeStart.IsZero() {
+ if len(fltr) != 0 {
+ fltr += " AND "
+ }
+ fltr += fmt.Sprintf(" answer_time>='%d'", timeStart)
+ }
+ if !timeEnd.IsZero() {
+ if len(fltr) != 0 {
+ fltr += " AND "
+ }
+ fltr += fmt.Sprintf(" answer_time<'%d'", timeEnd)
+ }
+ if ignoreErr {
+ if len(fltr) != 0 {
+ fltr += " AND "
+ }
+ fltr += "cost>-1"
+ }
+ if ignoreRated {
+ if len(fltr) != 0 {
+ fltr += " AND "
+ }
+ fltr += "cost<=0"
+ }
+ if len(fltr) != 0 {
+ q += fmt.Sprintf(" WHERE %s", fltr)
}
rows, err := self.Db.Query(q)
if err != nil {
@@ -772,7 +792,7 @@ func (self *SQLStorage) GetRatedCdrs(timeStart, timeEnd time.Time) ([]*utils.Rat
if err := json.Unmarshal(extraFields, &extraFieldsMp); err != nil {
return nil, err
}
- storCdr := &utils.RatedCDR{
+ storCdr := &utils.StoredCdr{
CgrId: cgrid, AccId: accid, CdrHost: cdrhost, CdrSource: cdrsrc, ReqType: reqtype, Direction: direction, Tenant: tenant,
TOR: tor, Account: account, Subject: subject, Destination: destination, AnswerTime: answerTime, Duration: time.Duration(duration),
ExtraFields: extraFieldsMp, MediationRunId: runid.String, Cost: cost.Float64,
@@ -783,7 +803,7 @@ func (self *SQLStorage) GetRatedCdrs(timeStart, timeEnd time.Time) ([]*utils.Rat
}
// Remove CDR data out of all CDR tables based on their cgrid
-func (self *SQLStorage) RemRatedCdrs(cgrIds []string) error {
+func (self *SQLStorage) RemStoredCdrs(cgrIds []string) error {
if len(cgrIds) == 0 {
return nil
}
diff --git a/engine/storage_test.go b/engine/storage_test.go
index 873cd4eec..29985a548 100644
--- a/engine/storage_test.go
+++ b/engine/storage_test.go
@@ -107,6 +107,17 @@ func TestCacheRefresh(t *testing.T) {
}
}
+// Install fails to detect them and starting server will panic, these tests will fix this
+func TestStoreInterfaces(t *testing.T) {
+ rds := new(RedisStorage)
+ var _ RatingStorage = rds
+ var _ AccountingStorage = rds
+ sql := new(SQLStorage)
+ var _ CdrStorage = sql
+ var _ LogStorage = sql
+}
+
+
/************************** Benchmarks *****************************/
func GetUB() *UserBalance {
diff --git a/mediator/mediator.go b/mediator/mediator.go
index bee664665..c7b950ca7 100644
--- a/mediator/mediator.go
+++ b/mediator/mediator.go
@@ -77,7 +77,7 @@ func (self *Mediator) getCostsFromDB(cgrid string) (cc *engine.CallCost, err err
}
// Retrive the cost from engine
-func (self *Mediator) getCostsFromRater(cdr *utils.RatedCDR) (*engine.CallCost, error) {
+func (self *Mediator) getCostsFromRater(cdr *utils.StoredCdr) (*engine.CallCost, error) {
cc := &engine.CallCost{}
var err error
if cdr.Duration == time.Duration(0) { // failed call, returning empty callcost, no error
@@ -109,7 +109,7 @@ func (self *Mediator) getCostsFromRater(cdr *utils.RatedCDR) (*engine.CallCost,
return cc, err
}
-func (self *Mediator) rateCDR(cdr *utils.RatedCDR) error {
+func (self *Mediator) rateCDR(cdr *utils.StoredCdr) error {
var qryCC *engine.CallCost
var errCost error
if cdr.ReqType == utils.PREPAID || cdr.ReqType == utils.POSTPAID {
@@ -128,21 +128,21 @@ func (self *Mediator) rateCDR(cdr *utils.RatedCDR) error {
}
// Forks original CDR based on original request plus runIds for extra mediation
-func (self *Mediator) MediateRawCDR(dbcdr utils.RawCDR) error {
+func (self *Mediator) RateCdr(dbcdr utils.RawCDR) error {
//engine.Logger.Debug(fmt.Sprintf("Mediating rawCdr: %v, duration: %d",dbcdr, dbcdr.GetDuration()))
- rtCdr, err := utils.NewRatedCDRFromRawCDR(dbcdr)
+ rtCdr, err := utils.NewStoredCdrFromRawCDR(dbcdr)
if err != nil {
return err
}
//engine.Logger.Debug(fmt.Sprintf("Have converted raw into rated: %v", rtCdr))
- cdrs := []*utils.RatedCDR{rtCdr} // Start with initial dbcdr, will add here all to be mediated
+ cdrs := []*utils.StoredCdr{rtCdr} // Start with initial dbcdr, will add here all to be mediated
for runIdx, runId := range self.cgrCfg.MediatorRunIds {
- forkedCdr, err := dbcdr.AsRatedCdr(self.cgrCfg.MediatorRunIds[runIdx], self.cgrCfg.MediatorReqTypeFields[runIdx], self.cgrCfg.MediatorDirectionFields[runIdx],
+ forkedCdr, err := dbcdr.AsStoredCdr(self.cgrCfg.MediatorRunIds[runIdx], self.cgrCfg.MediatorReqTypeFields[runIdx], self.cgrCfg.MediatorDirectionFields[runIdx],
self.cgrCfg.MediatorTenantFields[runIdx], self.cgrCfg.MediatorTORFields[runIdx], self.cgrCfg.MediatorAccountFields[runIdx],
self.cgrCfg.MediatorSubjectFields[runIdx], self.cgrCfg.MediatorDestFields[runIdx], self.cgrCfg.MediatorAnswerTimeFields[runIdx],
self.cgrCfg.MediatorDurationFields[runIdx], []string{}, true)
if err != nil { // Errors on fork, cannot calculate further, write that into db for later analysis
- self.cdrDb.SetRatedCdr(&utils.RatedCDR{CgrId: dbcdr.GetCgrId(), MediationRunId: runId, Cost: -1.0}, err.Error()) // Cannot fork CDR, important just runid and error
+ self.cdrDb.SetRatedCdr(&utils.StoredCdr{CgrId: dbcdr.GetCgrId(), MediationRunId: runId, Cost: -1.0}, err.Error()) // Cannot fork CDR, important just runid and error
continue
}
cdrs = append(cdrs, forkedCdr)
@@ -159,3 +159,16 @@ func (self *Mediator) MediateRawCDR(dbcdr utils.RawCDR) error {
}
return nil
}
+
+func (self *Mediator) RateCdrs(timeStart, timeEnd time.Time, rerateErrors, rerateRated bool) error {
+ cdrs, err := self.cdrDb.GetStoredCdrs(timeStart, timeEnd, !rerateErrors, !rerateRated)
+ if err != nil {
+ return err
+ }
+ for _, cdr := range cdrs {
+ if err := self.RateCdr(cdr); err != nil {
+ return err
+ }
+ }
+ return nil
+}
diff --git a/utils/apitpdata.go b/utils/apitpdata.go
index bc9475266..c12619561 100644
--- a/utils/apitpdata.go
+++ b/utils/apitpdata.go
@@ -309,11 +309,19 @@ type AttrExpFileCdrs struct {
CdrFormat string // Cdr output file format
TimeStart string // If provided, will represent the starting of the CDRs interval (>=)
TimeEnd string // If provided, will represent the end of the CDRs interval (<)
+ SkipErrors bool // Do not export errored CDRs
+ SkipRated bool // Do not export rated CDRs
RemoveFromDb bool // If true the CDRs will be also deleted after export
-
}
type ExportedFileCdrs struct {
ExportedFilePath string // Full path to the newly generated export file
NumberOfRecords int // Number of CDRs in the export file
}
+
+type AttrRateCdrs struct {
+ TimeStart string // Cdrs time start
+ TimeEnd string // Cdrs time end
+ RerateErrors bool // Rerate previous CDRs with errors (makes sense for reqtype rated and pseudoprepaid
+ RerateRated bool // Rerate CDRs which were previously rated (makes sense for reqtype rated and pseudoprepaid)
+}
diff --git a/utils/cgrcdr.go b/utils/cgrcdr.go
index 48120893f..544833608 100644
--- a/utils/cgrcdr.go
+++ b/utils/cgrcdr.go
@@ -107,14 +107,14 @@ func (cgrCdr CgrCdr) GetDuration() time.Duration {
// Used in mediation, fieldsMandatory marks whether missing field out of request represents error or can be ignored
// If the fields in parameters start with ^ their value is considered instead of dynamically retrieving it from CDR
-func (cgrCdr CgrCdr) AsRatedCdr(runId, reqTypeFld, directionFld, tenantFld, torFld, accountFld, subjectFld, destFld, answerTimeFld, durationFld string, extraFlds []string, fieldsMandatory bool) (*RatedCDR, error) {
+func (cgrCdr CgrCdr) AsStoredCdr(runId, reqTypeFld, directionFld, tenantFld, torFld, accountFld, subjectFld, destFld, answerTimeFld, durationFld string, extraFlds []string, fieldsMandatory bool) (*StoredCdr, error) {
if IsSliceMember([]string{runId, reqTypeFld, directionFld, tenantFld, torFld, accountFld, subjectFld, destFld, answerTimeFld, durationFld}, "") {
return nil, errors.New(fmt.Sprintf("%s:FieldName", ERR_MANDATORY_IE_MISSING)) // All input field names are mandatory
}
var err error
var hasKey bool
var aTimeStr, durStr string
- rtCdr := new(RatedCDR)
+ rtCdr := new(StoredCdr)
rtCdr.MediationRunId = runId
rtCdr.Cost = -1.0 // Default for non-rated CDR
if rtCdr.AccId, hasKey = cgrCdr[ACCID]; !hasKey {
diff --git a/utils/cgrcdr_test.go b/utils/cgrcdr_test.go
index 071c9ef11..2b55e2bb8 100644
--- a/utils/cgrcdr_test.go
+++ b/utils/cgrcdr_test.go
@@ -78,32 +78,32 @@ func TestCgrCdrFields(t *testing.T) {
}
}
-func TestCgrCdrAsRatedCdr(t *testing.T) {
+func TestCgrCdrAsStoredCdr(t *testing.T) {
cgrCdr := &CgrCdr{"accid": "dsafdsaf", "cdrhost": "192.168.1.1", "cdrsource": "source_test", "reqtype": "rated", "direction": "*out", "tenant": "cgrates.org", "tor": "call",
"account": "1001", "subject": "1001", "destination": "1002", "answer_time": "2013-11-07T08:42:26Z", "duration": "10",
"field_extr1": "val_extr1", "fieldextr2": "valextr2"}
- rtCdrOut, err := cgrCdr.AsRatedCdr("wholesale_run", "reqtype", "direction", "tenant", "tor", "account", "subject", "destination", "answer_time", "duration", []string{"field_extr1", "fieldextr2"}, true)
+ rtCdrOut, err := cgrCdr.AsStoredCdr("wholesale_run", "reqtype", "direction", "tenant", "tor", "account", "subject", "destination", "answer_time", "duration", []string{"field_extr1", "fieldextr2"}, true)
if err != nil {
t.Error("Unexpected error received", err)
}
- expctRatedCdr := &RatedCDR{CgrId: FSCgrId("dsafdsaf"), AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: "source_test", ReqType: "rated",
+ expctRatedCdr := &StoredCdr{CgrId: FSCgrId("dsafdsaf"), AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: "source_test", ReqType: "rated",
Direction: "*out", Tenant: "cgrates.org", TOR: "call", Account: "1001", Subject: "1001", Destination: "1002", AnswerTime: time.Unix(1383813746, 0).UTC(),
Duration: 10000000000, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, MediationRunId: "wholesale_run", Cost: -1}
if !reflect.DeepEqual(rtCdrOut, expctRatedCdr) {
t.Errorf("Received: %v, expected: %v", rtCdrOut, expctRatedCdr)
}
- rtCdrOut2, err := cgrCdr.AsRatedCdr("wholesale_run", "^postpaid", "^*in", "^cgrates.com", "^premium_call", "^first_account", "^first_subject", "destination", "^2013-12-07T08:42:26Z", "^12s", []string{"field_extr1", "fieldextr2"}, true)
+ rtCdrOut2, err := cgrCdr.AsStoredCdr("wholesale_run", "^postpaid", "^*in", "^cgrates.com", "^premium_call", "^first_account", "^first_subject", "destination", "^2013-12-07T08:42:26Z", "^12s", []string{"field_extr1", "fieldextr2"}, true)
if err != nil {
t.Error("Unexpected error received", err)
}
- expctRatedCdr2 := &RatedCDR{CgrId: FSCgrId("dsafdsaf"), AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: "source_test", ReqType: "postpaid",
+ expctRatedCdr2 := &StoredCdr{CgrId: FSCgrId("dsafdsaf"), AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: "source_test", ReqType: "postpaid",
Direction: "*in", Tenant: "cgrates.com", TOR: "premium_call", Account: "first_account", Subject: "first_subject", Destination: "1002",
AnswerTime: time.Date(2013, 12, 7, 8, 42, 26, 0, time.UTC), Duration: time.Duration(12) * time.Second,
ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, MediationRunId: "wholesale_run", Cost: -1}
if !reflect.DeepEqual(rtCdrOut2, expctRatedCdr2) {
t.Errorf("Received: %v, expected: %v", rtCdrOut2, expctRatedCdr2)
}
- _, err = cgrCdr.AsRatedCdr("wholesale_run", "dummy_header", "direction", "tenant", "tor", "account", "subject", "destination", "answer_time", "duration", []string{"field_extr1", "fieldextr2"}, true)
+ _, err = cgrCdr.AsStoredCdr("wholesale_run", "dummy_header", "direction", "tenant", "tor", "account", "subject", "destination", "answer_time", "duration", []string{"field_extr1", "fieldextr2"}, true)
if err == nil {
t.Error("Failed to detect missing header")
}
diff --git a/utils/rawcdr.go b/utils/rawcdr.go
index bf2cfbbf9..a91a4bf77 100644
--- a/utils/rawcdr.go
+++ b/utils/rawcdr.go
@@ -40,5 +40,5 @@ type RawCDR interface {
GetAnswerTime() (time.Time, error)
GetDuration() time.Duration
GetExtraFields() map[string]string //Stores extra CDR Fields
- AsRatedCdr(string, string, string, string, string, string, string, string, string, string, []string, bool) (*RatedCDR, error) // Based on fields queried will return a particular instance of RatedCDR
+ AsStoredCdr(string, string, string, string, string, string, string, string, string, string, []string, bool) (*StoredCdr, error) // Based on fields queried will return a particular instance of RatedCDR
}
diff --git a/utils/ratedcdr.go b/utils/storedcdr.go
similarity index 50%
rename from utils/ratedcdr.go
rename to utils/storedcdr.go
index da4846d78..e7c661482 100644
--- a/utils/ratedcdr.go
+++ b/utils/storedcdr.go
@@ -24,9 +24,9 @@ import (
"time"
)
-func NewRatedCDRFromRawCDR(rawcdr RawCDR) (*RatedCDR, error) {
+func NewStoredCdrFromRawCDR(rawcdr RawCDR) (*StoredCdr, error) {
var err error
- rtCdr := new(RatedCDR)
+ rtCdr := new(StoredCdr)
rtCdr.CgrId = rawcdr.GetCgrId()
rtCdr.AccId = rawcdr.GetAccId()
rtCdr.CdrHost = rawcdr.GetCdrHost()
@@ -49,7 +49,7 @@ func NewRatedCDRFromRawCDR(rawcdr RawCDR) (*RatedCDR, error) {
}
// Rated CDR as extracted from StorDb. Kinda standard of internal CDR, complies to CDR interface also
-type RatedCDR struct {
+type StoredCdr struct {
CgrId string
AccId string
CdrHost string
@@ -70,82 +70,82 @@ type RatedCDR struct {
// Methods maintaining RawCDR interface
-func (ratedCdr *RatedCDR) GetCgrId() string {
- return ratedCdr.CgrId
+func (storedCdr *StoredCdr) GetCgrId() string {
+ return storedCdr.CgrId
}
-func (ratedCdr *RatedCDR) GetAccId() string {
- return ratedCdr.AccId
+func (storedCdr *StoredCdr) GetAccId() string {
+ return storedCdr.AccId
}
-func (ratedCdr *RatedCDR) GetCdrHost() string {
- return ratedCdr.CdrHost
+func (storedCdr *StoredCdr) GetCdrHost() string {
+ return storedCdr.CdrHost
}
-func (ratedCdr *RatedCDR) GetCdrSource() string {
- return ratedCdr.CdrSource
+func (storedCdr *StoredCdr) GetCdrSource() string {
+ return storedCdr.CdrSource
}
-func (ratedCdr *RatedCDR) GetDirection() string {
- return ratedCdr.Direction
+func (storedCdr *StoredCdr) GetDirection() string {
+ return storedCdr.Direction
}
-func (ratedCdr *RatedCDR) GetSubject() string {
- return ratedCdr.Subject
+func (storedCdr *StoredCdr) GetSubject() string {
+ return storedCdr.Subject
}
-func (ratedCdr *RatedCDR) GetAccount() string {
- return ratedCdr.Account
+func (storedCdr *StoredCdr) GetAccount() string {
+ return storedCdr.Account
}
-func (ratedCdr *RatedCDR) GetDestination() string {
- return ratedCdr.Destination
+func (storedCdr *StoredCdr) GetDestination() string {
+ return storedCdr.Destination
}
-func (ratedCdr *RatedCDR) GetTOR() string {
- return ratedCdr.TOR
+func (storedCdr *StoredCdr) GetTOR() string {
+ return storedCdr.TOR
}
-func (ratedCdr *RatedCDR) GetTenant() string {
- return ratedCdr.Tenant
+func (storedCdr *StoredCdr) GetTenant() string {
+ return storedCdr.Tenant
}
-func (ratedCdr *RatedCDR) GetReqType() string {
- return ratedCdr.ReqType
+func (storedCdr *StoredCdr) GetReqType() string {
+ return storedCdr.ReqType
}
-func (ratedCdr *RatedCDR) GetAnswerTime() (time.Time, error) {
- return ratedCdr.AnswerTime, nil
+func (storedCdr *StoredCdr) GetAnswerTime() (time.Time, error) {
+ return storedCdr.AnswerTime, nil
}
-func (ratedCdr *RatedCDR) GetDuration() time.Duration {
- return ratedCdr.Duration
+func (storedCdr *StoredCdr) GetDuration() time.Duration {
+ return storedCdr.Duration
}
-func (ratedCdr *RatedCDR) GetExtraFields() map[string]string {
- return ratedCdr.ExtraFields
+func (storedCdr *StoredCdr) GetExtraFields() map[string]string {
+ return storedCdr.ExtraFields
}
-func (ratedCdr *RatedCDR) AsRatedCdr(runId, reqTypeFld, directionFld, tenantFld, torFld, accountFld, subjectFld, destFld, answerTimeFld, durationFld string, extraFlds []string, fieldsMandatory bool) (*RatedCDR, error) {
- return ratedCdr, nil
+func (storedCdr *StoredCdr) AsStoredCdr(runId, reqTypeFld, directionFld, tenantFld, torFld, accountFld, subjectFld, destFld, answerTimeFld, durationFld string, extraFlds []string, fieldsMandatory bool) (*StoredCdr, error) {
+ return storedCdr, nil
}
// Converts part of the rated Cdr as httpForm used to post remotely to CDRS
-func (ratedCdr *RatedCDR) AsRawCdrHttpForm() url.Values {
+func (storedCdr *StoredCdr) AsRawCdrHttpForm() url.Values {
v := url.Values{}
- v.Set(ACCID, ratedCdr.AccId)
- v.Set(CDRHOST, ratedCdr.CdrHost)
- v.Set(CDRSOURCE, ratedCdr.CdrSource)
- v.Set(REQTYPE, ratedCdr.ReqType)
- v.Set(DIRECTION, ratedCdr.Direction)
- v.Set(TENANT, ratedCdr.Tenant)
- v.Set(TOR, ratedCdr.TOR)
- v.Set(ACCOUNT, ratedCdr.Account)
- v.Set(SUBJECT, ratedCdr.Subject)
- v.Set(DESTINATION, ratedCdr.Destination)
- v.Set(ANSWER_TIME, ratedCdr.AnswerTime.String())
- v.Set(DURATION, strconv.FormatFloat(ratedCdr.Duration.Seconds(), 'f', -1, 64))
- for fld, val := range ratedCdr.ExtraFields {
+ v.Set(ACCID, storedCdr.AccId)
+ v.Set(CDRHOST, storedCdr.CdrHost)
+ v.Set(CDRSOURCE, storedCdr.CdrSource)
+ v.Set(REQTYPE, storedCdr.ReqType)
+ v.Set(DIRECTION, storedCdr.Direction)
+ v.Set(TENANT, storedCdr.Tenant)
+ v.Set(TOR, storedCdr.TOR)
+ v.Set(ACCOUNT, storedCdr.Account)
+ v.Set(SUBJECT, storedCdr.Subject)
+ v.Set(DESTINATION, storedCdr.Destination)
+ v.Set(ANSWER_TIME, storedCdr.AnswerTime.String())
+ v.Set(DURATION, strconv.FormatFloat(storedCdr.Duration.Seconds(), 'f', -1, 64))
+ for fld, val := range storedCdr.ExtraFields {
v.Set(fld, val)
}
return v
diff --git a/utils/ratedcdr_test.go b/utils/storedcdr_test.go
similarity index 88%
rename from utils/ratedcdr_test.go
rename to utils/storedcdr_test.go
index 7802924d9..c3fe07cf0 100644
--- a/utils/ratedcdr_test.go
+++ b/utils/storedcdr_test.go
@@ -24,28 +24,28 @@ import (
"time"
)
-func TestRatedCDRInterfaces(t *testing.T) {
- ratedCdr := new(RatedCDR)
+func TestStoredCdrInterfaces(t *testing.T) {
+ ratedCdr := new(StoredCdr)
var _ RawCDR = ratedCdr
}
-func TestNewRatedCDRFromRawCDR(t *testing.T) {
+func TestNewStoredCdrFromRawCDR(t *testing.T) {
cgrCdr := CgrCdr{"accid": "dsafdsaf", "cdrhost": "192.168.1.1", "cdrsource": "internal_test", "reqtype": "rated", "direction": "*out", "tenant": "cgrates.org", "tor": "call",
"account": "1001", "subject": "1001", "destination": "1002", "answer_time": "2013-11-07T08:42:26Z", "duration": "10",
"field_extr1": "val_extr1", "fieldextr2": "valextr2"}
- expctRtCdr := &RatedCDR{CgrId: FSCgrId(cgrCdr["accid"]), AccId: cgrCdr["accid"], CdrHost: cgrCdr["cdrhost"], CdrSource: cgrCdr["cdrsource"], ReqType: cgrCdr["reqtype"],
+ expctRtCdr := &StoredCdr{CgrId: FSCgrId(cgrCdr["accid"]), AccId: cgrCdr["accid"], CdrHost: cgrCdr["cdrhost"], CdrSource: cgrCdr["cdrsource"], ReqType: cgrCdr["reqtype"],
Direction: cgrCdr["direction"], Tenant: cgrCdr["tenant"], TOR: cgrCdr["tor"], Account: cgrCdr["account"], Subject: cgrCdr["subject"],
Destination: cgrCdr["destination"], AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), Duration: time.Duration(10) * time.Second,
ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, MediationRunId: DEFAULT_RUNID, Cost: -1}
- if rt, err := NewRatedCDRFromRawCDR(cgrCdr); err != nil {
+ if rt, err := NewStoredCdrFromRawCDR(cgrCdr); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(rt, expctRtCdr) {
t.Errorf("Received %v, expected: %v", rt, expctRtCdr)
}
}
-func TestRatedCdrFields(t *testing.T) {
- ratedCdr := RatedCDR{CgrId: FSCgrId("dsafdsaf"), AccId: "dsafdsaf", CdrHost: "192.168.1.1", ReqType: "rated", Direction: "*out", Tenant: "cgrates.org",
+func TestStoredCdrFields(t *testing.T) {
+ ratedCdr := StoredCdr{CgrId: FSCgrId("dsafdsaf"), AccId: "dsafdsaf", CdrHost: "192.168.1.1", ReqType: "rated", Direction: "*out", Tenant: "cgrates.org",
TOR: "call", Account: "1001", Subject: "1001", Destination: "1002", AnswerTime: time.Unix(1383813746, 0), Duration: 10,
ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
}
@@ -100,7 +100,7 @@ func TestRatedCdrFields(t *testing.T) {
}
func TestAsRawCdrHttpForm(t *testing.T) {
- ratedCdr := RatedCDR{CgrId: FSCgrId("dsafdsaf"), AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: "test", ReqType: "rated", Direction: "*out", Tenant: "cgrates.org",
+ ratedCdr := StoredCdr{CgrId: FSCgrId("dsafdsaf"), AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: "test", ReqType: "rated", Direction: "*out", Tenant: "cgrates.org",
TOR: "call", Account: "1001", Subject: "1001", Destination: "1002", AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC),
Duration: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
}