From 63385160b7316366c294375a54ec174b15c0d20d Mon Sep 17 00:00:00 2001 From: DanB Date: Mon, 26 Oct 2015 10:58:24 +0100 Subject: [PATCH 1/5] Suretax changes --- engine/suretax.go | 20 ++++---------------- general_tests/suretax_it_test.go | 2 +- utils/rsrfield_test.go | 11 +++++++++++ 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/engine/suretax.go b/engine/suretax.go index 20b30d4dd..4f4720ba4 100644 --- a/engine/suretax.go +++ b/engine/suretax.go @@ -26,7 +26,6 @@ import ( "fmt" "io/ioutil" "net/http" - "net/url" "strconv" "strings" @@ -130,17 +129,6 @@ type STRequestItem struct { TaxExemptionCodeList []string // Required. Tax Exemption to be applied to this item only. } -// Converts the request into the format SureTax expects -func (self *SureTaxRequest) AsHttpForm() (url.Values, error) { - jsnContent, err := json.Marshal(self) - if err != nil { - return nil, err - } - v := url.Values{} - v.Set("request", string(jsnContent)) - return v, nil -} - // SureTax Response type type SureTaxResponse struct { Successful string // Response will be either ‘Y' or ‘N' : Y = Success / Success with Item error N = Failure @@ -190,13 +178,13 @@ func SureTaxProcessCdr(cdr *StoredCdr) error { if err != nil { return err } - - body, err := json.Marshal(req) + jsnContent, err := json.Marshal(req) if err != nil { return err } - utils.Logger.Debug(fmt.Sprintf("###SureTax NewSureTaxRequest: %+v, ItemList: %+v\n", req, req.ItemList[0])) - resp, err := sureTaxClient.Post(stCfg.Url, "application/json", bytes.NewBuffer(body)) + utils.Logger.Debug(fmt.Sprintf("###SureTax NewSureTaxRequest to: %s, body: %+v, ItemList: %+v\n", stCfg.Url, req, req.ItemList[0])) + body := append([]byte("request="), jsnContent...) + resp, err := sureTaxClient.Post(stCfg.Url, "application/x-www-form-urlencoded", bytes.NewBuffer(body)) if err != nil { return err } diff --git a/general_tests/suretax_it_test.go b/general_tests/suretax_it_test.go index 5ade9bce0..ba4a44f25 100644 --- a/general_tests/suretax_it_test.go +++ b/general_tests/suretax_it_test.go @@ -137,7 +137,7 @@ func TestSTIProcessExternalCdr(t *testing.T) { AccId: "teststicdr1", CdrHost: "192.168.1.1", CdrSource: "STI_TEST", ReqType: utils.META_RATED, Direction: utils.OUT, Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "+14082342500", Destination: "+16268412300", Supplier: "SUPPL1", SetupTime: "2015-10-18T13:00:00Z", AnswerTime: "2015-10-18T13:00:00Z", - Usage: "15s", Pdd: "7.0", ExtraFields: map[string]string{"ClientNumber": "000000534", "": "valextr2"}, + Usage: "15s", Pdd: "7.0", ExtraFields: map[string]string{"CustomerNumber": "000000534", "ZipCode": ""}, } var reply string if err := stiRpc.Call("CdrsV2.ProcessExternalCdr", cdr, &reply); err != nil { diff --git a/utils/rsrfield_test.go b/utils/rsrfield_test.go index 02adf10bb..cc03bd21f 100644 --- a/utils/rsrfield_test.go +++ b/utils/rsrfield_test.go @@ -221,3 +221,14 @@ func TestRSRFieldsId(t *testing.T) { t.Errorf("Received id: %s", idRcv) } } + +func TestRSRCostDetails(t *testing.T) { + fieldsStr1 := `{"Direction":"*out","Category":"default_route","Tenant":"demo.cgrates.org","Subject":"voxbeam_premium","Account":"6335820713","Destination":"15143606781","TOR":"*voice","Cost":0.0007,"Timespans":[{"TimeStart":"2015-08-30T21:46:54Z","TimeEnd":"2015-08-30T21:47:06Z","Cost":0.00072,"RateInterval":{"Timing":{"Years":[],"Months":[],"MonthDays":[],"WeekDays":[],"StartTime":"00:00:00","EndTime":""},"Rating":{"ConnectFee":0,"RoundingMethod":"*middle","RoundingDecimals":5,"MaxCost":0,"MaxCostStrategy":"0","Rates":[{"GroupIntervalStart":0,"Value":0.0036,"RateIncrement":6000000000,"RateUnit":60000000000}]},"Weight":10},"DurationIndex":12000000000,"Increments":[{"Duration":6000000000,"Cost":0.00036,"BalanceInfo":{"UnitBalanceUuid":"","MoneyBalanceUuid":"40adda88-25d3-4009-b928-f39d61590439","AccountId":"*out:demo.cgrates.org:6335820713"},"BalanceRateInterval":null,"UnitInfo":null,"CompressFactor":2}],"MatchedSubject":"*out:demo.cgrates.org:default_route:voxbeam_premium","MatchedPrefix":"1514","MatchedDestId":"Canada","RatingPlanId":"RP_VOXBEAM_PREMIUM"}]}` + rsrField, err := NewRSRField(`~cost_details:s/"MatchedDestId":"(\w+)"/${1}/`) + if err != nil { + t.Error(err) + } + if parsedVal := rsrField.ParseValue(fieldsStr1); parsedVal != "Canada" { + t.Errorf("Expecting: Canada, received: %s", parsedVal) + } +} From 6494284b58df1dafcad7fbbbbb744a044e632936 Mon Sep 17 00:00:00 2001 From: DanB Date: Mon, 26 Oct 2015 19:07:03 +0100 Subject: [PATCH 2/5] Encapsulate SureTax Request and Response objects --- engine/suretax.go | 33 ++++++++++++++++++---------- engine/suretax_test.go | 6 ++--- general_tests/suretax_it_test.go | 18 +++++++++++++-- general_tests/tutorial_local_test.go | 2 +- 4 files changed, 42 insertions(+), 17 deletions(-) diff --git a/engine/suretax.go b/engine/suretax.go index 4f4720ba4..fda552d93 100644 --- a/engine/suretax.go +++ b/engine/suretax.go @@ -51,7 +51,7 @@ func NewSureTaxRequest(cdr *StoredCdr, stCfg *config.SureTaxCfg) (*SureTaxReques if len(definedTaxExtempt) != 0 { taxExempt = strings.Split(cdr.FieldsAsString(stCfg.TaxExemptionCodeList), ",") } - stReq := new(SureTaxRequest) + stReq := new(STRequest) stReq.ClientNumber = stCfg.ClientNumber stReq.BusinessUnit = "" // Export it to config stReq.ValidationKey = stCfg.ValidationKey @@ -85,11 +85,15 @@ func NewSureTaxRequest(cdr *StoredCdr, stCfg *config.SureTaxCfg) (*SureTaxReques TaxExemptionCodeList: taxExempt, }, } - return stReq, nil + return &SureTaxRequest{Request: stReq}, nil +} + +type SureTaxRequest struct { + Request *STRequest // SureTax Requires us to encapsulate the content into a request element } // SureTax Request type -type SureTaxRequest struct { +type STRequest struct { ClientNumber string // Client ID Number – provided by SureTax. Required. Max Len: 10 BusinessUnit string // Client’s Business Unit. Value for this field is not required. Max Len: 20 ValidationKey string // Validation Key provided by SureTax. Required for client access to API function. Max Len: 36 @@ -131,6 +135,10 @@ type STRequestItem struct { // SureTax Response type type SureTaxResponse struct { + D *STResponse // SureTax requires encapsulating reply into a D object +} + +type STResponse struct { Successful string // Response will be either ‘Y' or ‘N' : Y = Success / Success with Item error N = Failure ResponseCode int64 // ResponseCode: 9999 – Request was successful. 1101-1400 – Range of values for a failed request (no processing occurred) 9001 – Request was successful, but items within the request have errors. The specific items with errors are provided in the ItemMessages field. HeaderMessage string // Response message: For ResponseCode 9999 – “Success”For ResponseCode 9001 – “Success with Item errors”. For ResponseCode 1100-1400 – Unsuccessful / declined web request. @@ -164,6 +172,7 @@ type STTaxItem struct { } func SureTaxProcessCdr(cdr *StoredCdr) error { + fmt.Printf("SureTaxProcessCdr, cdr: %+v\n", cdr) stCfg := config.CgrConfig().SureTaxCfg() if stCfg == nil { return errors.New("Invalid SureTax configuration") @@ -182,34 +191,36 @@ func SureTaxProcessCdr(cdr *StoredCdr) error { if err != nil { return err } - utils.Logger.Debug(fmt.Sprintf("###SureTax NewSureTaxRequest to: %s, body: %+v, ItemList: %+v\n", stCfg.Url, req, req.ItemList[0])) - body := append([]byte("request="), jsnContent...) - resp, err := sureTaxClient.Post(stCfg.Url, "application/x-www-form-urlencoded", bytes.NewBuffer(body)) + fmt.Printf("NewSureTaxRequest: %s\n", string(jsnContent)) + resp, err := sureTaxClient.Post(stCfg.Url, "application/json", bytes.NewBuffer(jsnContent)) if err != nil { return err } defer resp.Body.Close() respBody, err := ioutil.ReadAll(resp.Body) if err != nil { + fmt.Printf("Unexpected response body received, error: %s\n", err.Error()) return err } if resp.StatusCode > 299 { + fmt.Printf("Unexpected code received: %d\n", resp.StatusCode) return fmt.Errorf("Unexpected status code received: %d", resp.StatusCode) } + fmt.Printf("Received raw answer from SureTax: %s\n", string(respBody)) var stResp SureTaxResponse if err := json.Unmarshal(respBody, &stResp); err != nil { return err } - utils.Logger.Debug(fmt.Sprintf("###SureTax received response: %+v\n", stResp)) - if stResp.ResponseCode != 9999 { - cdr.ExtraInfo = stResp.HeaderMessage + fmt.Printf("Received answer from SureTax: %+v\n", stResp) + if stResp.D.ResponseCode != 9999 { + cdr.ExtraInfo = stResp.D.HeaderMessage return nil // No error because the request was processed by SureTax, error will be in the ExtraInfo } // Write cost to CDR if !stCfg.IncludeLocalCost { - cdr.Cost = utils.Round(stResp.TotalTax, config.CgrConfig().RoundingDecimals, utils.ROUNDING_MIDDLE) + cdr.Cost = utils.Round(stResp.D.TotalTax, config.CgrConfig().RoundingDecimals, utils.ROUNDING_MIDDLE) } else { - cdr.Cost = utils.Round(cdr.Cost+stResp.TotalTax, config.CgrConfig().RoundingDecimals, utils.ROUNDING_MIDDLE) + cdr.Cost = utils.Round(cdr.Cost+stResp.D.TotalTax, config.CgrConfig().RoundingDecimals, utils.ROUNDING_MIDDLE) } // Add response into extra fields to be available for later review cdr.ExtraFields[utils.META_SURETAX] = string(respBody) diff --git a/engine/suretax_test.go b/engine/suretax_test.go index a01b203a3..5273d6fa3 100644 --- a/engine/suretax_test.go +++ b/engine/suretax_test.go @@ -40,7 +40,7 @@ func TestNewSureTaxRequest(t *testing.T) { stCfg.ClientNumber = "000000000" stCfg.ValidationKey = "19491161-F004-4F44-BDB3-E976D6739A64" stCfg.Timezone = time.UTC - eSureTaxRequest := &SureTaxRequest{ + eSureTaxRequest := &SureTaxRequest{Request: &STRequest{ ClientNumber: "000000000", ValidationKey: "19491161-F004-4F44-BDB3-E976D6739A64", DataYear: "2013", @@ -69,10 +69,10 @@ func TestNewSureTaxRequest(t *testing.T) { TaxExemptionCodeList: []string{}, }, }, - } + }} if stReq, err := NewSureTaxRequest(cdr, stCfg); err != nil { t.Error(err) } else if !reflect.DeepEqual(eSureTaxRequest, stReq) { - t.Errorf("Expecting: %+v, received: %+v", eSureTaxRequest.ItemList[0], stReq.ItemList[0]) + t.Errorf("Expecting: %+v, received: %+v", eSureTaxRequest.Request.ItemList[0], stReq.Request.ItemList[0]) } } diff --git a/general_tests/suretax_it_test.go b/general_tests/suretax_it_test.go index ba4a44f25..ddcb87065 100644 --- a/general_tests/suretax_it_test.go +++ b/general_tests/suretax_it_test.go @@ -76,6 +76,7 @@ func TestSTIResetStorDb(t *testing.T) { } } +/* // Start CGR Engine func TestSTIStartEngine(t *testing.T) { if !*testSureTax { @@ -85,6 +86,7 @@ func TestSTIStartEngine(t *testing.T) { t.Fatal(err) } } +*/ // Connect rpc client to rater func TestSTIRpcConn(t *testing.T) { @@ -92,7 +94,7 @@ func TestSTIRpcConn(t *testing.T) { return } var err error - stiRpc, err = jsonrpc.Dial("tcp", stiCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed + stiRpc, err = jsonrpc.Dial("tcp", "172.16.254.70:2012") // We connect over JSON so we can also troubleshoot if needed // stiCfg.RPCJSONListen if err != nil { t.Fatal(err) } @@ -145,7 +147,7 @@ func TestSTIProcessExternalCdr(t *testing.T) { } else if reply != utils.OK { t.Error("Unexpected reply received: ", reply) } - time.Sleep(time.Duration(*waitRater) * time.Millisecond) + time.Sleep(time.Duration(2) * time.Second) } func TestSTIGetCdrs(t *testing.T) { @@ -163,8 +165,19 @@ func TestSTIGetCdrs(t *testing.T) { t.Errorf("Unexpected Cost for CDR: %+v", cdrs[0]) } } + req = utils.RpcCdrsFilter{RunIds: []string{utils.META_SURETAX}, Accounts: []string{"1001"}} + if err := stiRpc.Call("ApierV2.GetCdrs", req, &cdrs); err != nil { + t.Error("Unexpected error: ", err.Error()) + } else if len(cdrs) != 1 { + t.Error("Unexpected number of CDRs returned: ", len(cdrs)) + } else { + if cdrs[0].Cost != 0.012 { + t.Errorf("Unexpected Cost for CDR: %+v", cdrs[0]) + } + } } +/* func TestSTIStopCgrEngine(t *testing.T) { if !*testSureTax { return @@ -173,3 +186,4 @@ func TestSTIStopCgrEngine(t *testing.T) { t.Error(err) } } +*/ diff --git a/general_tests/tutorial_local_test.go b/general_tests/tutorial_local_test.go index d4e404915..255acc4cf 100644 --- a/general_tests/tutorial_local_test.go +++ b/general_tests/tutorial_local_test.go @@ -663,7 +663,7 @@ func TestTutLocalCostErrors(t *testing.T) { } else if reply != utils.OK { t.Error("Unexpected reply received: ", reply) } - + time.Sleep(time.Duration(*waitRater) * time.Millisecond) // Give time for CDR to be processed req = utils.RpcCdrsFilter{RunIds: []string{utils.META_DEFAULT}, Accounts: []string{cdr2.Account}, DestPrefixes: []string{cdr2.Destination}} if err := tutLocalRpc.Call("ApierV2.GetCdrs", req, &cdrs); err != nil { t.Error("Unexpected error: ", err.Error()) From 1025d7aceb3235751febdbaea0c2ad369734c38b Mon Sep 17 00:00:00 2001 From: DanB Date: Tue, 27 Oct 2015 19:00:26 +0100 Subject: [PATCH 3/5] Revert SureTax tests to generic ones --- engine/suretax.go | 11 +++++------ general_tests/suretax_it_test.go | 6 +----- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/engine/suretax.go b/engine/suretax.go index fda552d93..516e557ad 100644 --- a/engine/suretax.go +++ b/engine/suretax.go @@ -172,7 +172,6 @@ type STTaxItem struct { } func SureTaxProcessCdr(cdr *StoredCdr) error { - fmt.Printf("SureTaxProcessCdr, cdr: %+v\n", cdr) stCfg := config.CgrConfig().SureTaxCfg() if stCfg == nil { return errors.New("Invalid SureTax configuration") @@ -191,7 +190,7 @@ func SureTaxProcessCdr(cdr *StoredCdr) error { if err != nil { return err } - fmt.Printf("NewSureTaxRequest: %s\n", string(jsnContent)) + utils.Logger.Debug(fmt.Sprintf("NewSureTaxRequest: %s\n", string(jsnContent))) resp, err := sureTaxClient.Post(stCfg.Url, "application/json", bytes.NewBuffer(jsnContent)) if err != nil { return err @@ -199,19 +198,19 @@ func SureTaxProcessCdr(cdr *StoredCdr) error { defer resp.Body.Close() respBody, err := ioutil.ReadAll(resp.Body) if err != nil { - fmt.Printf("Unexpected response body received, error: %s\n", err.Error()) + utils.Logger.Debug(fmt.Sprintf("Unexpected response body received, error: %s\n", err.Error())) return err } if resp.StatusCode > 299 { - fmt.Printf("Unexpected code received: %d\n", resp.StatusCode) + utils.Logger.Debug(fmt.Sprintf("Unexpected code received: %d\n", resp.StatusCode)) return fmt.Errorf("Unexpected status code received: %d", resp.StatusCode) } - fmt.Printf("Received raw answer from SureTax: %s\n", string(respBody)) + utils.Logger.Debug(fmt.Sprintf("Received raw answer from SureTax: %s\n", string(respBody))) var stResp SureTaxResponse if err := json.Unmarshal(respBody, &stResp); err != nil { return err } - fmt.Printf("Received answer from SureTax: %+v\n", stResp) + utils.Logger.Debug(fmt.Sprintf("Received answer from SureTax: %+v\n", stResp)) if stResp.D.ResponseCode != 9999 { cdr.ExtraInfo = stResp.D.HeaderMessage return nil // No error because the request was processed by SureTax, error will be in the ExtraInfo diff --git a/general_tests/suretax_it_test.go b/general_tests/suretax_it_test.go index ddcb87065..f45a3c4ce 100644 --- a/general_tests/suretax_it_test.go +++ b/general_tests/suretax_it_test.go @@ -76,7 +76,6 @@ func TestSTIResetStorDb(t *testing.T) { } } -/* // Start CGR Engine func TestSTIStartEngine(t *testing.T) { if !*testSureTax { @@ -86,7 +85,6 @@ func TestSTIStartEngine(t *testing.T) { t.Fatal(err) } } -*/ // Connect rpc client to rater func TestSTIRpcConn(t *testing.T) { @@ -94,7 +92,7 @@ func TestSTIRpcConn(t *testing.T) { return } var err error - stiRpc, err = jsonrpc.Dial("tcp", "172.16.254.70:2012") // We connect over JSON so we can also troubleshoot if needed // stiCfg.RPCJSONListen + stiRpc, err = jsonrpc.Dial("tcp", stiCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed if err != nil { t.Fatal(err) } @@ -177,7 +175,6 @@ func TestSTIGetCdrs(t *testing.T) { } } -/* func TestSTIStopCgrEngine(t *testing.T) { if !*testSureTax { return @@ -186,4 +183,3 @@ func TestSTIStopCgrEngine(t *testing.T) { t.Error(err) } } -*/ From b1e1e2410036a55394b92ba7803f3afdc3a5b2f5 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 26 Oct 2015 13:46:03 +0200 Subject: [PATCH 4/5] simple stack logger --- engine/balances.go | 2 +- utils/logger.go | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/engine/balances.go b/engine/balances.go index 26b51b42a..689774179 100644 --- a/engine/balances.go +++ b/engine/balances.go @@ -651,7 +651,7 @@ func (bc BalanceChain) SaveDirtyBalances(acc *Account) { allowNegative := "" disabled := "" if b.account != nil { // only publish modifications for balances with account set - //debug.PrintStack() + //utils.LogStack() accountId = b.account.Id allowNegative = strconv.FormatBool(b.account.AllowNegative) disabled = strconv.FormatBool(b.account.Disabled) diff --git a/utils/logger.go b/utils/logger.go index d5300b93b..cabbe068b 100644 --- a/utils/logger.go +++ b/utils/logger.go @@ -22,6 +22,7 @@ import ( "fmt" "log" "log/syslog" + "runtime" ) var Logger LoggerInterface @@ -85,3 +86,9 @@ func (sl *StdLogger) Warning(m string) (err error) { log.Print("[WARNING]" + m) return } + +func LogStack() { + buf := make([]byte, 300) + runtime.Stack(buf, false) + Logger.Debug(string(buf)) +} From f6ccd50bb5956164a7ef317433d6ca255bba920c Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 28 Oct 2015 09:20:38 +0200 Subject: [PATCH 5/5] update callcost to use rounded timespans cost should provide a fix for #260 --- engine/callcost.go | 12 ++++++++++++ engine/calldesc.go | 13 ++----------- engine/calldesc_test.go | 22 ++++++++++++++++++++++ engine/loader_csv_test.go | 12 ++++++++---- engine/timespans.go | 6 +----- sessionmanager/session.go | 2 ++ 6 files changed, 47 insertions(+), 20 deletions(-) diff --git a/engine/callcost.go b/engine/callcost.go index 238619491..c692fe963 100644 --- a/engine/callcost.go +++ b/engine/callcost.go @@ -152,3 +152,15 @@ func (cc *CallCost) AsJSON() string { ccJson, _ := json.Marshal(cc) return string(ccJson) } + +func (cc *CallCost) UpdateCost() { + cost := 0.0 + if cc.deductConnectFee { // add back the connectFee + cost += cc.GetConnectFee() + } + for _, ts := range cc.Timespans { + cost += ts.getCost() + cost = utils.Round(cost, globalRoundingDecimals, utils.ROUNDING_MIDDLE) // just get rid of the extra decimals + } + cc.Cost = cost +} diff --git a/engine/calldesc.go b/engine/calldesc.go index 0b69069bb..1cc40eb89 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -83,7 +83,7 @@ var ( // Exported method to set the storage getter. func SetRatingStorage(sg RatingStorage) { - ratingStorage = sg + ratingStorage = sg } func SetAccountingStorage(ag AccountingStorage) { @@ -636,16 +636,7 @@ func (cd *CallDescriptor) debit(account *Account, dryRun bool, goNegative bool) utils.Logger.Err(fmt.Sprintf(" Error getting cost for account key <%s>: %s", cd.GetAccountKey(), err.Error())) return nil, err } - cost := 0.0 - // calculate call cost after balances - if cc.deductConnectFee { // add back the connectFee - cost += cc.GetConnectFee() - } - for _, ts := range cc.Timespans { - cost += ts.getCost() - cost = utils.Round(cost, globalRoundingDecimals, utils.ROUNDING_MIDDLE) // just get rid of the extra decimals - } - cc.Cost = cost + cc.UpdateCost() cc.Timespans.Compress() //log.Printf("OUT CC: ", cc) return diff --git a/engine/calldesc_test.go b/engine/calldesc_test.go index 9c1aa542b..81a281075 100644 --- a/engine/calldesc_test.go +++ b/engine/calldesc_test.go @@ -555,6 +555,28 @@ func TestGetCostWithMaxCost(t *testing.T) { t.Errorf("Expected %v was %v", expected, cc.Cost) } } +func TestGetCostRoundingIssue(t *testing.T) { + ap, _ := ratingStorage.GetActionPlans("TOPUP10_AT") + for _, at := range ap { + at.Execute() + } + cd := &CallDescriptor{ + Direction: "*out", + Category: "call", + Tenant: "cgrates.org", + Subject: "dy", + Account: "dy", + Destination: "0723123113", + TimeStart: time.Date(2015, 10, 26, 13, 29, 27, 0, time.UTC), + TimeEnd: time.Date(2015, 10, 26, 13, 29, 51, 0, time.UTC), + MaxCostSoFar: 0, + } + cc, err := cd.GetCost() + expected := 0.17 + if cc.Cost != expected || err != nil { + t.Errorf("Expected %v was %+v", expected, cc) + } +} func TestMaxSessionTimeWithMaxCostFree(t *testing.T) { ap, _ := ratingStorage.GetActionPlans("TOPUP10_AT") diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 794836b6c..d81143d97 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -70,6 +70,7 @@ RT_UK_Mobile_BIG5_PKG,0.01,0,20s,20s,0s RT_UK_Mobile_BIG5,0.01,0.10,1s,1s,0s R_URG,0,0,1,1,0 MX,0,1,1s,1s,0 +DY,0.15,0.05,60s,1s,0s ` destinationRates = ` RT_STANDARD,GERMANY,R1,*middle,4,0, @@ -91,6 +92,7 @@ DATA_RATE,*any,LANDLINE_OFFPEAK,*middle,4,0, RT_URG,URG,R_URG,*middle,4,0, MX_FREE,RET,MX,*middle,4,10,*free MX_DISC,RET,MX,*middle,4,10,*disconnect +RT_DY,RET,DY,*up,2,0, ` ratingPlans = ` STANDARD,RT_STANDARD,WORKDAYS_00,10 @@ -115,6 +117,7 @@ RP_MX,MX_DISC,WORKDAYS_00,10 RP_MX,MX_FREE,WORKDAYS_18,10 GER_ONLY,GER,*any,10 ANY_PLAN,DATA_RATE,*any,10 +DY_PLAN,RT_DY,*any,10 ` ratingProfiles = ` *out,CUSTOMER_1,0,rif:from:tm,2012-01-01T00:00:00Z,PREMIUM,danb, @@ -141,6 +144,7 @@ ANY_PLAN,DATA_RATE,*any,10 *out,cgrates.org,call,nt,2012-02-28T00:00:00Z,GER_ONLY,, *in,cgrates.org,LCR_STANDARD,max,2013-03-23T00:00:00Z,RP_MX,, *out,cgrates.org,call,money,2015-02-28T00:00:00Z,EVENING,, +*out,cgrates.org,call,dy,2015-02-28T00:00:00Z,DY_PLAN,, ` sharedGroups = ` SG1,*any,*lowest, @@ -391,7 +395,7 @@ func TestLoadTimimgs(t *testing.T) { } func TestLoadRates(t *testing.T) { - if len(csvr.rates) != 13 { + if len(csvr.rates) != 14 { t.Error("Failed to load rates: ", len(csvr.rates)) } rate := csvr.rates["R1"].RateSlots[0] @@ -461,7 +465,7 @@ func TestLoadRates(t *testing.T) { } func TestLoadDestinationRates(t *testing.T) { - if len(csvr.destinationRates) != 14 { + if len(csvr.destinationRates) != 15 { t.Error("Failed to load destinationrates: ", len(csvr.destinationRates)) } drs := csvr.destinationRates["RT_STANDARD"] @@ -609,7 +613,7 @@ func TestLoadDestinationRates(t *testing.T) { } func TestLoadRatingPlans(t *testing.T) { - if len(csvr.ratingPlans) != 13 { + if len(csvr.ratingPlans) != 14 { t.Error("Failed to load rating plans: ", len(csvr.ratingPlans)) } rplan := csvr.ratingPlans["STANDARD"] @@ -781,7 +785,7 @@ func TestLoadRatingPlans(t *testing.T) { } func TestLoadRatingProfiles(t *testing.T) { - if len(csvr.ratingProfiles) != 21 { + if len(csvr.ratingProfiles) != 22 { t.Error("Failed to load rating profiles: ", len(csvr.ratingProfiles), csvr.ratingProfiles) } rp := csvr.ratingProfiles["*out:test:0:trp"] diff --git a/engine/timespans.go b/engine/timespans.go index a497ab4ef..24df295d7 100644 --- a/engine/timespans.go +++ b/engine/timespans.go @@ -263,11 +263,7 @@ func (ts *TimeSpan) getCost() float64 { ts.Cost = utils.Round(cost, ts.RateInterval.Rating.RoundingDecimals, ts.RateInterval.Rating.RoundingMethod) return ts.Cost } else { - cost := 0.0 - // some increments may have 0 cost because of the max cost strategy - for _, inc := range ts.Increments { - cost += inc.Cost - } + cost := ts.Increments.GetTotalCost() if ts.RateInterval != nil && ts.RateInterval.Rating != nil { return utils.Round(cost, ts.RateInterval.Rating.RoundingDecimals, ts.RateInterval.Rating.RoundingMethod) } else { diff --git a/sessionmanager/session.go b/sessionmanager/session.go index 813058f65..faa5c9b7d 100644 --- a/sessionmanager/session.go +++ b/sessionmanager/session.go @@ -233,6 +233,8 @@ func (s *Session) SaveOperations() { firstCC.Merge(cc) //utils.Logger.Debug(fmt.Sprintf("AFTER MERGE: %s", utils.ToJSON(firstCC))) } + // make sure we have rounded timespans final cost + firstCC.UpdateCost() var reply string err := s.sessionManager.CdrSrv().LogCallCost(&engine.CallCostLog{