diff --git a/apier/v2/cdre.go b/apier/v2/cdre.go new file mode 100644 index 000000000..a867b1a48 --- /dev/null +++ b/apier/v2/cdre.go @@ -0,0 +1,130 @@ +/* +Real-time Charging System for Telecom & ISP environments +Copyright (C) 2012-2015 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/cdre" + "github.com/cgrates/cgrates/utils" +) + +// Export Cdrs to file +func (self *ApierV2) ExportCdrsToFile(attr utils.AttrExportCdrsToFile, reply *utils.ExportedFileCdrs) error { + var err error + 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.ERR_NOT_FOUND) + } + } + cdrFormat := exportTemplate.CdrFormat + if attr.CdrFormat != nil && len(*attr.CdrFormat) != 0 { + cdrFormat = strings.ToLower(*attr.CdrFormat) + } + if !utils.IsSliceMember(utils.CdreCdrFormats, cdrFormat) { + return fmt.Errorf("%s:%s", utils.ERR_MANDATORY_IE_MISSING, "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.ERR_SERVER_ERROR, "Invalid") + } + } + exportDir := exportTemplate.ExportDir + if attr.ExportDir != nil && len(*attr.ExportDir) != 0 { + exportDir = *attr.ExportDir + } + 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, cdrFormat) + if attr.ExportFileName != nil && len(*attr.ExportFileName) != 0 { + fileName = *attr.ExportFileName + } + filePath := path.Join(exportDir, fileName) + if cdrFormat == utils.DRYRUN { + filePath = utils.DRYRUN + } + dataUsageMultiplyFactor := exportTemplate.DataUsageMultiplyFactor + if attr.DataUsageMultiplyFactor != nil && *attr.DataUsageMultiplyFactor != 0.0 { + dataUsageMultiplyFactor = *attr.DataUsageMultiplyFactor + } + smsUsageMultiplyFactor := exportTemplate.SmsUsageMultiplyFactor + if attr.SmsUsageMultiplyFactor != nil && *attr.SmsUsageMultiplyFactor != 0.0 { + smsUsageMultiplyFactor = *attr.SmsUsageMultiplyFactor + } + costMultiplyFactor := exportTemplate.CostMultiplyFactor + if attr.CostMultiplyFactor != nil && *attr.CostMultiplyFactor != 0.0 { + costMultiplyFactor = *attr.CostMultiplyFactor + } + costShiftDigits := exportTemplate.CostShiftDigits + if attr.CostShiftDigits != nil { + costShiftDigits = *attr.CostShiftDigits + } + roundingDecimals := exportTemplate.CostRoundingDecimals + if attr.RoundDecimals != nil { + roundingDecimals = *attr.RoundDecimals + } + maskDestId := exportTemplate.MaskDestId + if attr.MaskDestinationId != nil && len(*attr.MaskDestinationId) != 0 { + maskDestId = *attr.MaskDestinationId + } + maskLen := exportTemplate.MaskLength + if attr.MaskLength != nil { + maskLen = *attr.MaskLength + } + cdrsFltr, err := attr.RpcCdrsFilter.AsCdrsFilter() + if err != nil { + return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) + } + cdrs, _, err := self.CdrDb.GetStoredCdrs(cdrsFltr) + if err != nil { + return err + } else if len(cdrs) == 0 { + *reply = utils.ExportedFileCdrs{ExportedFilePath: ""} + return nil + } + cdrexp, err := cdre.NewCdrExporter(cdrs, self.CdrDb, exportTemplate, cdrFormat, fieldSep, exportId, dataUsageMultiplyFactor, smsUsageMultiplyFactor, + costMultiplyFactor, costShiftDigits, roundingDecimals, self.Config.RoundingDecimals, maskDestId, maskLen, self.Config.HttpSkipTlsVerify) + if err != nil { + return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) + } + if cdrexp.TotalExportedCdrs() == 0 { + *reply = utils.ExportedFileCdrs{ExportedFilePath: ""} + return nil + } + if err := cdrexp.WriteToFile(filePath); err != nil { + return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) + } + *reply = utils.ExportedFileCdrs{ExportedFilePath: filePath, TotalRecords: len(cdrs), TotalCost: cdrexp.TotalCost(), FirstOrderId: cdrexp.FirstOrderId(), LastOrderId: cdrexp.LastOrderId()} + if !attr.SuppressCgrIds { + reply.ExportedCgrIds = cdrexp.PositiveExports() + reply.UnexportedCgrIds = cdrexp.NegativeExports() + } + return nil +} diff --git a/console/cdrs_export.go b/console/cdrs_export.go index 16d309b67..85ac82e89 100644 --- a/console/cdrs_export.go +++ b/console/cdrs_export.go @@ -23,7 +23,7 @@ import "github.com/cgrates/cgrates/utils" func init() { c := &CmdExportCdrs{ name: "cdrs_export", - rpcMethod: "ApierV1.ExportCdrsToFile", + rpcMethod: "ApierV2.ExportCdrsToFile", } commands[c.Name()] = c c.CommandExecuter = &CommandExecuter{c} @@ -33,7 +33,7 @@ func init() { type CmdExportCdrs struct { name string rpcMethod string - rpcParams *utils.AttrExpFileCdrs + rpcParams *utils.AttrExportCdrsToFile *CommandExecuter } @@ -47,7 +47,7 @@ func (self *CmdExportCdrs) RpcMethod() string { func (self *CmdExportCdrs) RpcParams() interface{} { if self.rpcParams == nil { - self.rpcParams = &utils.AttrExpFileCdrs{} + self.rpcParams = &utils.AttrExportCdrsToFile{} } return self.rpcParams } @@ -57,5 +57,5 @@ func (self *CmdExportCdrs) PostprocessRpcParams() error { } func (self *CmdExportCdrs) RpcResult() interface{} { - return &utils.ExportedFileCdrs{} + return &utils.AttrExportCdrsToFile{} } diff --git a/data/conf/cgrates/cgrates.json b/data/conf/cgrates/cgrates.json index c1698295e..58ee095c0 100644 --- a/data/conf/cgrates/cgrates.json +++ b/data/conf/cgrates/cgrates.json @@ -107,7 +107,7 @@ // "accounts": [], // filter on CDR Account fields // "subjects": [], // filter on CDR Subject fields // "destination_prefixes": [], // filter on CDR Destination prefixes -// "usage_interval": [], // filter on CDR Usage +// "usage_interval": [], // filter on CDR Usage // "mediation_run_ids": [], // filter on CDR MediationRunId fields // "rated_accounts": [], // filter on CDR RatedAccount fields // "rated_subjects": [], // filter on CDR RatedSubject fields @@ -143,7 +143,7 @@ // {"tag":"SetupTime", "cdr_field_id": "setup_time", "type": "cdrfield", "value": "setup_time", "layout": "2006-01-02T15:04:05Z07:00"}, // {"tag":"AnswerTime", "cdr_field_id": "answer_time", "type": "cdrfield", "value": "answer_time", "layout": "2006-01-02T15:04:05Z07:00"}, // {"tag":"Usage", "cdr_field_id": "usage", "type": "cdrfield", "value": "usage"}, -// {"tag":"Cost", "cdr_field_id": "cost", "type": "cdrfield", "value": "cost"}, +// {"tag":"Cost", "cdr_field_id": "cost", "type": "cdrfield", "value": "cost"}, // ], // "trailer_fields": [], // template of the exported trailer fields // } @@ -241,11 +241,11 @@ //"mailer": { -// "server": "localhost", // the server to use when sending emails out +// "server": "localhost:25", // 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 //}, -} \ No newline at end of file +} diff --git a/data/docker/devel/Dockerfile b/data/docker/devel/Dockerfile index bb1f5567b..46a252d80 100644 --- a/data/docker/devel/Dockerfile +++ b/data/docker/devel/Dockerfile @@ -1,33 +1,26 @@ FROM debian:latest MAINTAINER Radu Fericean, rif@cgrates.org -RUN apt-get -y update -# install dependency -RUN apt-get -y install git bzr mercurial redis-server mysql-server python-pycurl python-mysqldb wget - -# add cgrates user -RUN useradd -c CGRateS -d /var/run/cgrates -s /bin/false -r cgrates +# set mysql password +RUN echo 'mysql-server mysql-server/root_password password CGRateS.org' | debconf-set-selections && echo 'mysql-server mysql-server/root_password_again password CGRateS.org' | debconf-set-selections # add freeswitch gpg key -RUN gpg --keyserver pool.sks-keyservers.net --recv-key D76EDC7725E010CF - -# add freeswitch apt key -RUN gpg -a --export D76EDC7725E010CF | apt-key add - +RUN gpg --keyserver pool.sks-keyservers.net --recv-key D76EDC7725E010CF && gpg -a --export D76EDC7725E010CF | apt-key add - # add freeswitch apt repo -RUN cd /etc/apt/sources.list.d/; wget http://apt.itsyscom.com/conf/freeswitch.apt.list - -# install freeswitch -RUN apt-get update; apt-get -y install freeswitch-meta-vanilla freeswitch-mod-json-cdr +RUN echo 'deb http://files.freeswitch.org/repo/deb/debian/ jessie main' > /etc/apt/sources.list.d/freeswitch.list + +# install dependencies +RUN apt-get -y update && apt-get -y install git bzr mercurial redis-server mysql-server python-pycurl python-mysqldb postgresql postgresql-client sudo wget freeswitch-meta-vanilla + +# add cgrates user +RUN useradd -c CGRateS -d /var/run/cgrates -s /bin/false -r cgrates # install golang -RUN wget -qO- https://storage.googleapis.com/golang/go1.3.linux-amd64.tar.gz | tar xzf - -C /root/ - -# add startup script -ADD start.sh /root/start.sh +RUN wget -qO- https://storage.googleapis.com/golang/go1.4.2.linux-amd64.tar.gz | tar xzf - -C /root/ # cleanup RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* # set start command -CMD /root/start.sh +CMD /root/code/src/github.com/cgrates/cgrates/data/docker/devel/start.sh diff --git a/data/docker/devel/README.md b/data/docker/devel/README.md new file mode 100644 index 000000000..622bf1f9f --- /dev/null +++ b/data/docker/devel/README.md @@ -0,0 +1,11 @@ +Docker +======= + +From the project root: + +``` bash +# build the image +docker build -t cgrates data/docker/devel +# create the container +docker run --rm -itv `pwd`:/root/code/src/github.com/cgrates/cgrates --name cgr cgrates +``` diff --git a/data/docker/devel/start.sh b/data/docker/devel/start.sh index 725906d4a..9ff15ffcf 100755 --- a/data/docker/devel/start.sh +++ b/data/docker/devel/start.sh @@ -3,27 +3,26 @@ export GOPATH=/root/code export PATH=$PATH:$GOROOT/bin:$GOPATH/bin /etc/init.d/mysql start -/usr/share/cgrates/tutorials/fs_csv/freeswitch/etc/init.d/freeswitch start -mysqladmin -u root password CGRateS.org +/etc/init.d/postgresql start +/etc/init.d/redis-server start # create a link to data dir ln -s /root/code/src/github.com/cgrates/cgrates/data /usr/share/cgrates - -# expand freeswitch json conf -tar xzf /usr/share/cgrates/tutorials/fs_json/freeswitch/etc/freeswitch_conf.tar.gz - -# expand freeswitch csv -tar xzf /usr/share/cgrates/tutorials/fs_csv/freeswitch/etc/freeswitch_conf.tar.gz - # create link to cgrates dir ln -s /root/code/src/github.com/cgrates/cgrates /root/cgr +cd /usr/share/cgrates/storage/mysql && ./setup_cgr_db.sh root CGRateS.org +cd /usr/share/cgrates/storage/postgres && ./setup_cgr_db.sh + +# build and install cgrates +/root/cgr/update_external_libs.sh +go install github.com/cgrates/cgrates + # create cgr-engine link ln -s /root/code/bin/cgr-engine /usr/bin/cgr-engine -cd /usr/share/cgrates/storage/mysql; ./setup_cgr_db.sh root CGRateS.org localhost -cd / - -cgr-engine -config /root/cgr/data/conf/cgrates.cfg & +# expand freeswitch conf +cd /usr/share/cgrates/tutorials/fs_evsock/freeswitch/etc/ && tar xzf freeswitch_conf.tar.gz +cd /root/cgr bash --rcfile /root/.bashrc diff --git a/data/docker/prod/Dockerfile b/data/docker/prod/Dockerfile index a81c08ff9..6cd94e46a 100644 --- a/data/docker/prod/Dockerfile +++ b/data/docker/prod/Dockerfile @@ -2,39 +2,30 @@ FROM debian:latest MAINTAINER Radu Fericean, rif@cgrates.org RUN apt-get -y update -# install dependencies -RUN apt-get -y install git redis-server mysql-server python-mysqldb wget - +# set mysql password +RUN echo 'mysql-server mysql-server/root_password password CGRateS.org' | debconf-set-selections && echo 'mysql-server mysql-server/root_password_again password CGRateS.org' | debconf-set-selections + # add freeswitch gpg key -RUN gpg --keyserver pool.sks-keyservers.net --recv-key D76EDC7725E010CF - -# add freeswitch apt key -RUN gpg -a --export D76EDC7725E010CF | apt-key add - +RUN gpg --keyserver pool.sks-keyservers.net --recv-key D76EDC7725E010CF && gpg -a --export D76EDC7725E010CF | apt-key add - # add freeswitch apt repo -RUN cd /etc/apt/sources.list.d/; wget http://apt.itsyscom.com/conf/freeswitch.apt.list - -# install freeswitch -RUN apt-get update; apt-get -y install freeswitch-meta-vanilla freeswitch-mod-json-cdr +RUN echo 'deb http://files.freeswitch.org/repo/deb/debian/ jessie main' > /etc/apt/sources.list.d/freeswitch.list + +# install dependencies +RUN apt-get update && apt-get -y install redis-server mysql-server python-pycurl python-mysqldb postgresql postgresql-client sudo wget git freeswitch-meta-vanilla # add cgrates apt-key -RUN wget -O - http://apt.itsyscom.com/conf/cgrates.gpg.key | apt-key add - +RUN wget -qO- http://apt.itsyscom.com/conf/cgrates.gpg.key | apt-key add - # add cgrates repo -RUN cd /etc/apt/sources.list.d/; wget http://apt.itsyscom.com/conf/cgrates.apt.list +RUN cd /etc/apt/sources.list.d/; wget -q http://apt.itsyscom.com/conf/cgrates.apt.list # install cgrates -RUN apt-get update; apt-get -y install cgrates - -# copy .my.cnf (do not enable it) -ADD my.cnf /my.cnf - -# add startup script -ADD start.sh /root/start.sh +RUN apt-get update && apt-get -y install cgrates # cleanup RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* # set start command -CMD /root/start.sh +CMD /root/code/data/docker/prod/start.sh diff --git a/data/docker/prod/README.md b/data/docker/prod/README.md new file mode 100644 index 000000000..4d38990d1 --- /dev/null +++ b/data/docker/prod/README.md @@ -0,0 +1,11 @@ +Docker +======= + +From the project root: + +``` bash +# build the image +docker build -t cgrates data/docker/prod +# create the container +docker run --rm -itv `pwd`:/root/code --name cgr cgrates +``` diff --git a/data/docker/prod/start.sh b/data/docker/prod/start.sh index db1f185d1..0b9cc0406 100755 --- a/data/docker/prod/start.sh +++ b/data/docker/prod/start.sh @@ -1,6 +1,11 @@ /etc/init.d/mysql start -/usr/share/cgrates/tutorials/fs_csv/freeswitch/etc/init.d/freeswitch start -mysqladmin -u root password CGRateS.org -cd /usr/share/cgrates/storage/mysql; ./setup_cgr_db.sh root CGRateS.org localhost -cd / +/etc/init.d/postgresql start +/etc/init.d/redis-server start + +cd /usr/share/cgrates/storage/mysql && ./setup_cgr_db.sh root CGRateS.org +cd /usr/share/cgrates/storage/postgres && ./setup_cgr_db.sh + +/usr/share/cgrates/tutorials/fs_evsock/freeswitch/etc/init.d/freeswitch start +/usr/share/cgrates/tutorials/fs_evsock/cgrates/etc/init.d/cgrates start + bash --rcfile /root/.bashrc diff --git a/data/kamailio/etc/kamailio/kamailio-cgrates.cfg b/data/kamailio/etc/kamailio/kamailio-cgrates.cfg index 0d88e83a1..5f2b70bfe 100644 --- a/data/kamailio/etc/kamailio/kamailio-cgrates.cfg +++ b/data/kamailio/etc/kamailio/kamailio-cgrates.cfg @@ -25,6 +25,11 @@ event_route[dialog:end] { route(CGR_CALL_END); } +event_route[dialog:failed] { + $avp(s:missed_call) = 1; + route(CGR_CALL_END); +} + # Send AUTH_REQUEST to CGRateS route[CGR_AUTH_REQUEST] { # Auth INVITEs with CGRateS @@ -114,14 +119,21 @@ route[CGR_CALL_END] { xlog("Charging controller unreachable"); exit; } - $var(callDur) = $TS - $dlg(start_ts); + + # As default the duration is 0. + # If missed_call avp doesn't exists system send the correct duration + $var(callDur) = 0; + if( $avp(s:missed_call) == $null){ + $var(callDur) = $TS - $dlg(start_ts); + } + evapi_async_relay("{\"event\":\"CGR_CALL_END\", \"callid\":\"$dlg(callid)\", \"from_tag\":\"$dlg(from_tag)\", \"cgr_reqtype\":\"$dlg_var(cgrReqType)\", \"cgr_tenant\":\"$dlg_var(cgrTenant)\", - \"cgr_account\":\"$dlg_var(cgrAccount)\", + \"cgr_account\":\"$dlg_var(cgrAccount)\", \"cgr_destination\":\"$dlg_var(cgrDestination)\", \"cgr_answertime\":\"$dlg(start_ts)\", \"cgr_duration\":\"$var(callDur)\"}"); -} \ No newline at end of file +} diff --git a/data/kamailio/etc/kamailio/kamailio.cfg b/data/kamailio/etc/kamailio/kamailio.cfg index df3c69c14..76409a528 100644 --- a/data/kamailio/etc/kamailio/kamailio.cfg +++ b/data/kamailio/etc/kamailio/kamailio.cfg @@ -51,8 +51,6 @@ loadmodule "dialog.so" loadmodule "xhttp.so" loadmodule "jsonrpc-s.so" - - # ----------------- setting module-specific parameters --------------- # ----- mi_fifo params ----- @@ -93,6 +91,10 @@ modparam("usrloc", "nat_bflag", FLB_NATB) modparam("htable", "htable", "users=>size=8;") modparam("htable", "htable", "cgrconn=>size=1;") + +modparam("evapi", "workers", 2) +modparam("evapi", "bind_addr", "127.0.0.1:8448") + ####### Routing Logic ######## include_file "kamailio-cgrates.cfg" diff --git a/docs/tariff_plans.rst b/docs/tariff_plans.rst index 8c9344837..8979f1e38 100644 --- a/docs/tariff_plans.rst +++ b/docs/tariff_plans.rst @@ -1,50 +1,58 @@ 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. +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. +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 | -+------------+-----+-----------+-------------+----------------------+----------------+----------------------+ +.. csv-table:: + :file: ../data/tariffplans/tutorial/RatingProfiles.csv + :header-rows: 1 Tenant - Used to distinguish between carriers if more than one share the same database in the CGRates system. + 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. + 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. + 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. + 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. + 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 +4.2.2. Rating Plans ~~~~~~~~~~~~~~~~~~~ -This file makes links between a ratings and timings so each of them can be described once and various combinations are made possible. +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 | -+----------+----------------+--------------+--------+ +.. csv-table:: + :file: ../data/tariffplans/tutorial/RatingPlans.csv + :header-rows: 1 Tag A string by which this rates timing will be referenced in other places by. @@ -53,20 +61,21 @@ RatesTag 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. + 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. +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 | -+---------------------+-----------------+------------+-------+-------------+ + +.. csv-table:: + :file: ../data/tariffplans/tutorial/Rates.csv + :header-rows: 1 Tag @@ -74,7 +83,8 @@ Tag DestinationsTag The destination tag which these rates apply to. ConnectFee - The price to be charged once at the beginning of the call to the specified destination. + 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 @@ -84,42 +94,34 @@ BillingUnit ~~~~~~~~~~~~~~ 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 | -+-----------------+--------+-----------+-----------+----------+ +.. csv-table:: + :file: ../data/tariffplans/tutorial/Timings.csv + :header-rows: 1 Tag A string by which 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. + 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. + 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. + 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. + 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 | -+------------+--------+ +The destinations are binding together various prefixes / caller ids to define a +logical destination group. A prefix can appear in multiple destination groups. +.. csv-table:: + :file: ../data/tariffplans/tutorial/Destinations.csv + :header-rows: 1 Tag A string by which this destination will be referenced in other places by. Prefix @@ -128,23 +130,24 @@ Prefix 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. +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. +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 | -+------------+---------+-----------+------------------+------------------+ - +.. csv-table:: + :file: ../data/tariffplans/tutorial/AccountActions.csv + :header-rows: 1 Tenant - Used to distinguish between carriers if more than one share the same database in the CGRates system. + 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 @@ -157,14 +160,15 @@ ActionTriggersTag 4.2.7 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. +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 | ThresholdType | ThresholdValue | Recurrent | MinSleep | BalanceTag | BalanceType | BalanceDirection | BalanceCategory | BalanceDestinationTag | BalanceRatingSubject | BalanceSharedGroup | BalanceExpiryTime | BalanceTimingTags | BalanceWeight | StatsMinQueuedItems | ActionsTag | Weight | -+===================+===============+================+===========+==========+============+=============+==================+=================+=======================+======================+====================+===================+===================+===============+=====================+=============+========+ -| STANDARD_TRIGGERS | \*min_balance | 2 | false | 0 | | \*monetary | \*out | | | | | | | | | LOG_WARNING | 10 | -| STANDARD_TRIGGERS | \*max_counter | 5 | false | 0 | | \*monetary | \*out | | FS_USERS | | | | | | | LOG_WARNING | 10 | -+-------------------+---------------+----------------+-----------+----------+------------+-------------+------------------+-----------------+-----------------------+----------------------+--------------------+-------------------+-------------------+---------------+---------------------+-------------+--------+ +.. csv-table:: + :file: ../data/tariffplans/tutorial/ActionTriggers.csv + :header-rows: 1 Tag A string by which this action trigger will be referenced in other places by. @@ -187,13 +191,15 @@ ThresholdValue The value of the balance counter that will trigger this action. Recurrent(Boolean) - In case of trigger we can fire recurrent while it's active, or only the first time. + In case of trigger we can fire recurrent while it's active, or only the + first time. MinSleep When Threshold is triggered we can sleep for the time specified. BalanceTag - Specifies the balance counter by which this action will be triggered. Can be: + Specifies the balance counter by which this action will be triggered. Can + be: + **MONETARY** + **SMS** @@ -232,22 +238,22 @@ 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. + 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. 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.a + 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.a -4.2.8. Action timings -~~~~~~~~~~~~~~~~~~~~~ +4.2.8. Action Plans +~~~~~~~~~~~~~~~~~~~ -+--------------+------------+------------------+--------+ -| Tag | ActionsTag | TimingTag | Weight | -+==============+============+==================+========+ -| STANDARD_ABO | SOME | WEEKLY_SAME_TIME | 10 | -+--------------+------------+------------------+--------+ -| STANDARD_ABO | SOME | WEEKLY_SAME_TIME | 10 | -+--------------+------------+------------------+--------+ +.. csv-table:: + :file: ../data/tariffplans/tutorial/ActionPlans.csv + :header-rows: 1 Tag A string by which this action timing will be referenced in other places by. @@ -256,50 +262,87 @@ ActionsTag 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. + 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.9. 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 | -+--------+-------------+------------+-------+----------------+-----------+------------+---------------+--------+ + +.. csv-table:: + :file: ../data/tariffplans/tutorial/Actions.csv + :header-rows: 1 + Tag A string by which 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 + + **CALL_URL_ASYNC**: Send a http request to the following url Asynchronous + + **CALL_URL**: Send a http request to the following url + + **CDRLOG**: Log the current action in the storeDB + + **DEBIT**: Debit account balance. + + **LOG**: Logs the other action values (for debugging purposes). + + **MAIL_ASYNC**: Send a email to the direction + + **RESET_ALL_COUNTERS**: Sets all counters to 0 + + **RESET_COUNTER**: Sets the counter for the BalanceTag to 0 + + **RESET_POSTPAID**: Set account to postpaid, reset all it's balances. + + **RESET_PREPAID**: Set account to prepaid, reset all it's balances. + + **RESET_TRIGGERS**: Marks all action triggers as ready to be executed. + + **SET_POSTPAID**: Sets account to postpaid, maintains 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. + + **TOPUP**: Add account balance. If the specific balance is not defined, define it (example: minutes per destination). + + **TOPUP_RESET**: Add account balance. If previous balance found of the same type, reset it before adding. + +ExtraParameters: + In Extra Parameter field you can define a argument for the action. In case + of call_url Action, extraParameter will be the url action. In case of + mail_async the email that you want to receive. BalanceTag The balance on which the action will operate Units The units which will be operated on the balance BalanceTag. + +BalanceType + + Specifies the balance type for this action: + + + **\*voice**: units of call minutes + + **\*sms**: units of SMS + + **\*data**: units of data + + **\*monetary**: units of money + +BalanceDirection + Can be **\*in** or **\*out** for the INBOUND and OUTBOUND calls. + 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. + This field is used only if the balanceTag is MINUTES. Specifies the + destination of the minutes to be operated. + +RatingSubject + The ratingSubject of the Actions + +SharedGroup + In case of the account uses any shared group for the balances. + +ExpiryTime + + +TimingTags + Timming tag when the action can be executed. Default ALL. + +Units + Number of units for decrease the balance. Only use if BalanceType is voice. + +BalanceWeight + Weight - If there are multiple actions in a group, they will be executed in the order of their weight (smaller first). + If there are multiple actions in a group, they will be executed in the order + of their weight (smaller first). + 4.2.10. Derived Chargers ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -307,12 +350,9 @@ Weight For each call we can bill more than one time, for that we need to use the following options: -+-----------+-------------+----------+---------+---------+--------------+-----------+--------------+----------------+-------------+---------------+--------------+--------------+------------------+----------------+-----------------+------------+ -| Direction | Tenant | Category | Account | Subject | RunId | RunFilter | ReqTypeField | DirectionField | TenantField | CategoryField | AccountField | SubjectField | DestinationField | SetupTimeField | AnswerTimeField | UsageField | -+===========+=============+==========+=========+=========+==============+===========+==============+================+=============+===============+==============+==============+==================+================+=================+============+ -| \*out | cgrates.org | call | 1001 | 1001 | derived_run1 | | ^rated | \*default | \*default | \*default | \*default | ^1002 | \*default | \*default | \*default | \*default | -+-----------+-------------+----------+---------+---------+--------------+-----------+--------------+----------------+-------------+---------------+--------------+--------------+------------------+----------------+-----------------+------------+ - +.. csv-table:: + :file: ../data/tariffplans/tutorial/DerivedChargers.csv + :header-rows: 1 In derived charges we have 2 different kind of options, filters, and actions: diff --git a/engine/calldesc.go b/engine/calldesc.go index 8809da0b5..38d9e97c1 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -405,6 +405,10 @@ func (cd *CallDescriptor) GetDuration() time.Duration { Creates a CallCost structure with the cost information calculated for the received CallDescriptor. */ func (cd *CallDescriptor) GetCost() (*CallCost, error) { + // check for 0 duration + if cd.TimeEnd.Sub(cd.TimeStart) == 0 { + return cd.CreateCallCost(), nil + } if cd.DurationIndex < cd.TimeEnd.Sub(cd.TimeStart) { cd.DurationIndex = cd.TimeEnd.Sub(cd.TimeStart) } @@ -429,18 +433,10 @@ func (cd *CallDescriptor) GetCost() (*CallCost, error) { cost += ts.getCost() } //startIndex := len(fmt.Sprintf("%s:%s:%s:", cd.Direction, cd.Tenant, cd.Category)) - cc := &CallCost{ - Direction: cd.Direction, - Category: cd.Category, - Tenant: cd.Tenant, - Account: cd.Account, - Destination: cd.Destination, - Subject: cd.Subject, - Cost: cost, - Timespans: timespans, - deductConnectFee: cd.LoopIndex == 0, - TOR: cd.TOR, - } + cc := cd.CreateCallCost() + cc.Cost = cost + cc.Timespans = timespans + // global rounding roundingDecimals, roundingMethod := cc.GetLongestRounding() cc.Cost = utils.Round(cc.Cost, roundingDecimals, roundingMethod) @@ -526,6 +522,9 @@ func (cd *CallDescriptor) GetMaxSessionDuration() (duration time.Duration, err e // Interface method used to add/substract an amount of cents or bonus seconds (as returned by GetCost method) // from user's money balance. func (cd *CallDescriptor) debit(account *Account, dryRun bool, goNegative bool) (cc *CallCost, err error) { + if cd.TimeEnd.Sub(cd.TimeStart) == 0 { + return cd.CreateCallCost(), nil + } if !dryRun { defer accountingStorage.SetAccount(account) } diff --git a/engine/calldesc_test.go b/engine/calldesc_test.go index c778d16ad..2c4f95ac1 100644 --- a/engine/calldesc_test.go +++ b/engine/calldesc_test.go @@ -229,6 +229,17 @@ func TestGetCost(t *testing.T) { } } +func TestGetCostZero(t *testing.T) { + t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC) + t2 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC) + cd := &CallDescriptor{Direction: "*out", Category: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2, LoopIndex: 0} + result, _ := cd.GetCost() + expected := &CallCost{Tenant: "vdf", Subject: "rif", Destination: "0256", Cost: 0} + if result.Cost != expected.Cost || result.GetConnectFee() != 0 { + t.Errorf("Expected %v was %v", expected, result) + } +} + func TestGetCostTimespans(t *testing.T) { t1 := time.Date(2013, time.October, 8, 9, 23, 2, 0, time.UTC) t2 := time.Date(2013, time.October, 8, 9, 24, 27, 0, time.UTC) diff --git a/general_tests/costs1_test.go b/general_tests/costs1_test.go index d9a177d7e..b662cee90 100644 --- a/general_tests/costs1_test.go +++ b/general_tests/costs1_test.go @@ -104,7 +104,6 @@ func TestCosts1GetCost1(t *testing.T) { } } -/* func TestCosts1GetCostZeroDuration(t *testing.T) { tStart, _ := utils.ParseTimeDetectLayout("2013-08-07T17:30:00Z") tEnd, _ := utils.ParseTimeDetectLayout("2013-08-07T17:30:00Z") @@ -124,7 +123,6 @@ func TestCosts1GetCostZeroDuration(t *testing.T) { t.Error("Wrong cost returned: ", cc.Cost) } } -*/ /* FixMe func TestCosts1GetCost2(t *testing.T) { diff --git a/utils/apitpdata.go b/utils/apitpdata.go index f39c6e538..8c50bef0d 100644 --- a/utils/apitpdata.go +++ b/utils/apitpdata.go @@ -1116,6 +1116,24 @@ func (self *RpcCdrsFilter) AsCdrsFilter() (*CdrsFilter, error) { return cdrFltr, nil } +type AttrExportCdrsToFile struct { + CdrFormat *string // Cdr output file format + FieldSeparator *string // Separator used between fields + ExportId *string // Optional exportid + ExportDir *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|*xml:instance_name> + 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) + CostMultiplyFactor *float64 // Multiply the cost before export, eg: apply VAT + CostShiftDigits *int // If defined it will shift cost digits before applying rouding (eg: convert from Eur->cents), -1 to use general config ones + RoundDecimals *int // Overwrite configured roundDecimals with this dynamically, -1 to use general config ones + MaskDestinationId *string // Overwrite configured MaskDestId + MaskLength *int // Overwrite configured MaskLength, -1 to use general config ones + SuppressCgrIds bool // Disable CgrIds reporting in reply/ExportedCgrIds and reply/UnexportedCgrIds + RpcCdrsFilter // Inherit the CDR filter attributes +} + type AttrSetActions struct { ActionsId string // Actions id Overwrite bool // If previously defined, will be overwritten