diff --git a/engine/suretax.go b/engine/suretax.go index 20b30d4dd..516e557ad 100644 --- a/engine/suretax.go +++ b/engine/suretax.go @@ -26,7 +26,6 @@ import ( "fmt" "io/ioutil" "net/http" - "net/url" "strconv" "strings" @@ -52,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 @@ -86,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 @@ -130,19 +133,12 @@ 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 { + 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. @@ -190,38 +186,40 @@ 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("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 { + utils.Logger.Debug(fmt.Sprintf("Unexpected response body received, error: %s\n", err.Error())) return err } if resp.StatusCode > 299 { + utils.Logger.Debug(fmt.Sprintf("Unexpected code received: %d\n", resp.StatusCode)) return fmt.Errorf("Unexpected status code received: %d", resp.StatusCode) } + 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 } - utils.Logger.Debug(fmt.Sprintf("###SureTax received response: %+v\n", stResp)) - if stResp.ResponseCode != 9999 { - cdr.ExtraInfo = stResp.HeaderMessage + 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 } // 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 5ade9bce0..f45a3c4ce 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 { @@ -145,7 +145,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,6 +163,16 @@ 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) { diff --git a/general_tests/tutorial_local_test.go b/general_tests/tutorial_local_test.go index 01052bcff..9f5e2dcb5 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()) 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) + } +}