diff --git a/config/config_defaults.go b/config/config_defaults.go index ce43d451e..59dbede2d 100644 --- a/config/config_defaults.go +++ b/config/config_defaults.go @@ -292,6 +292,7 @@ const CGRATES_CFG_JSON = ` "url": "", // API url "client_number": "", // client number, provided by SureTax "validation_key": "", // validation key provided by SureTax + "business_unit": "", // client’s Business Unit "timezone": "Local", // convert the time of the events to this timezone before sending request out "include_local_cost": false, // sum local calculated cost with tax one in final cost "return_file_code": "0", // default or Quote purposes <0|Q> diff --git a/config/config_json_test.go b/config/config_json_test.go index 703307599..4c1c0e690 100644 --- a/config/config_json_test.go +++ b/config/config_json_test.go @@ -478,6 +478,7 @@ func TestDfSureTaxJsonCfg(t *testing.T) { Url: utils.StringPointer(""), Client_number: utils.StringPointer(""), Validation_key: utils.StringPointer(""), + Business_unit: utils.StringPointer(""), Timezone: utils.StringPointer("Local"), Include_local_cost: utils.BoolPointer(false), Return_file_code: utils.StringPointer("0"), diff --git a/config/libconfig_json.go b/config/libconfig_json.go index 92f3faa20..07c960c33 100644 --- a/config/libconfig_json.go +++ b/config/libconfig_json.go @@ -269,6 +269,7 @@ type SureTaxJsonCfg struct { Url *string Client_number *string Validation_key *string + Business_unit *string Timezone *string Include_local_cost *bool Return_file_code *string diff --git a/config/suretaxconfig.go b/config/suretaxconfig.go index 5ab6d23f7..3346eeaa8 100644 --- a/config/suretaxconfig.go +++ b/config/suretaxconfig.go @@ -46,6 +46,7 @@ type SureTaxCfg struct { Url string ClientNumber string ValidationKey string + BusinessUnit string Timezone *time.Location // Convert the time of the events to this timezone before sending request out IncludeLocalCost bool ReturnFileCode string @@ -82,6 +83,9 @@ func (self *SureTaxCfg) loadFromJsonCfg(jsnCfg *SureTaxJsonCfg) error { if jsnCfg.Validation_key != nil { self.ValidationKey = *jsnCfg.Validation_key } + if jsnCfg.Business_unit != nil { + self.BusinessUnit = *jsnCfg.Business_unit + } if jsnCfg.Timezone != nil { if self.Timezone, err = time.LoadLocation(*jsnCfg.Timezone); err != nil { return err diff --git a/engine/suretax.go b/engine/suretax.go index 516e557ad..f58758699 100644 --- a/engine/suretax.go +++ b/engine/suretax.go @@ -53,7 +53,7 @@ func NewSureTaxRequest(cdr *StoredCdr, stCfg *config.SureTaxCfg) (*SureTaxReques } stReq := new(STRequest) stReq.ClientNumber = stCfg.ClientNumber - stReq.BusinessUnit = "" // Export it to config + stReq.BusinessUnit = stCfg.BusinessUnit stReq.ValidationKey = stCfg.ValidationKey stReq.DataYear = strconv.Itoa(aTimeLoc.Year()) stReq.DataMonth = strconv.Itoa(int(aTimeLoc.Month())) @@ -85,11 +85,21 @@ func NewSureTaxRequest(cdr *StoredCdr, stCfg *config.SureTaxCfg) (*SureTaxReques TaxExemptionCodeList: taxExempt, }, } - return &SureTaxRequest{Request: stReq}, nil + jsnContent, err := json.Marshal(stReq) + if err != nil { + return nil, err + } + return &SureTaxRequest{Request: string(jsnContent)}, nil } +// SureTax JSON Request type SureTaxRequest struct { - Request *STRequest // SureTax Requires us to encapsulate the content into a request element + Request string `json:"request"` // SureTax Requires us to encapsulate the content into a request element +} + +// SureTax JSON Response +type SureTaxResponse struct { + D string // SureTax requires encapsulating reply into a D object } // SureTax Request type @@ -133,18 +143,13 @@ type STRequestItem struct { TaxExemptionCodeList []string // Required. Tax Exemption to be applied to this item only. } -// 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. + ResponseCode string // 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. ItemMessages []*STItemMessage // This field contains a list of items that were not able to be processed due to bad or invalid data (see Response Code of “9001”). ClientTracking string // Client transaction tracking provided in web request. - TotalTax float64 // Total Tax – a total of all taxes included in the TaxList + TotalTax string // Total Tax – a total of all taxes included in the TaxList TransId int // Transaction ID – provided by SureTax GroupList []*STGroup // contains one-to-many Groups } @@ -166,9 +171,9 @@ type STGroup struct { // Part of the SureTax Response type STTaxItem struct { - TaxTypeCode string // Tax Type Code - TaxTypeDesc string // Tax Type Description - TaxAmount float64 // Tax Amount + TaxTypeCode string // Tax Type Code + TaxTypeDesc string // Tax Type Description + TaxAmount string // Tax Amount } func SureTaxProcessCdr(cdr *StoredCdr) error { @@ -198,30 +203,38 @@ func SureTaxProcessCdr(cdr *StoredCdr) error { 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())) + //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)) + //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 { + //utils.Logger.Debug(fmt.Sprintf("Received raw answer from SureTax: %s\n", string(respBody))) + var respFull SureTaxResponse + if err := json.Unmarshal(respBody, &respFull); err != nil { return err } - utils.Logger.Debug(fmt.Sprintf("Received answer from SureTax: %+v\n", stResp)) - if stResp.D.ResponseCode != 9999 { - cdr.ExtraInfo = stResp.D.HeaderMessage + //utils.Logger.Debug(fmt.Sprintf("Received answer from SureTax: %+v\n", respFull)) + var stResp STResponse + if err := json.Unmarshal([]byte(respFull.D), &stResp); err != nil { + return err + } + if stResp.ResponseCode != "9999" { + cdr.ExtraInfo = stResp.HeaderMessage return nil // No error because the request was processed by SureTax, error will be in the ExtraInfo } // Write cost to CDR + totalTax, err := strconv.ParseFloat(stResp.TotalTax, 64) + if err != nil { + cdr.ExtraInfo = err.Error() + } if !stCfg.IncludeLocalCost { - cdr.Cost = utils.Round(stResp.D.TotalTax, config.CgrConfig().RoundingDecimals, utils.ROUNDING_MIDDLE) + cdr.Cost = utils.Round(totalTax, config.CgrConfig().RoundingDecimals, utils.ROUNDING_MIDDLE) } else { - cdr.Cost = utils.Round(cdr.Cost+stResp.D.TotalTax, config.CgrConfig().RoundingDecimals, utils.ROUNDING_MIDDLE) + cdr.Cost = utils.Round(cdr.Cost+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) + cdr.ExtraFields[utils.META_SURETAX] = respFull.D return nil } diff --git a/engine/suretax_test.go b/engine/suretax_test.go index 5273d6fa3..c6f897814 100644 --- a/engine/suretax_test.go +++ b/engine/suretax_test.go @@ -19,6 +19,7 @@ along with this program. If not, see package engine import ( + "encoding/json" "reflect" "testing" "time" @@ -40,7 +41,7 @@ func TestNewSureTaxRequest(t *testing.T) { stCfg.ClientNumber = "000000000" stCfg.ValidationKey = "19491161-F004-4F44-BDB3-E976D6739A64" stCfg.Timezone = time.UTC - eSureTaxRequest := &SureTaxRequest{Request: &STRequest{ + eSTRequest := &STRequest{ ClientNumber: "000000000", ValidationKey: "19491161-F004-4F44-BDB3-E976D6739A64", DataYear: "2013", @@ -69,10 +70,12 @@ func TestNewSureTaxRequest(t *testing.T) { TaxExemptionCodeList: []string{}, }, }, - }} + } + jsnReq, _ := json.Marshal(eSTRequest) + eSureTaxRequest := &SureTaxRequest{Request: string(jsnReq)} if stReq, err := NewSureTaxRequest(cdr, stCfg); err != nil { t.Error(err) } else if !reflect.DeepEqual(eSureTaxRequest, stReq) { - t.Errorf("Expecting: %+v, received: %+v", eSureTaxRequest.Request.ItemList[0], stReq.Request.ItemList[0]) + t.Errorf("Expecting: %s, received: %s", string(eSureTaxRequest.Request), string(stReq.Request)) } } diff --git a/general_tests/suretax_it_test.go b/general_tests/suretax_it_test.go index f45a3c4ce..b5eacd153 100644 --- a/general_tests/suretax_it_test.go +++ b/general_tests/suretax_it_test.go @@ -169,7 +169,7 @@ func TestSTIGetCdrs(t *testing.T) { } else if len(cdrs) != 1 { t.Error("Unexpected number of CDRs returned: ", len(cdrs)) } else { - if cdrs[0].Cost != 0.012 { + if cdrs[0].Cost != 0.0027 { t.Errorf("Unexpected Cost for CDR: %+v", cdrs[0]) } }