mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 10:06:24 +05:00
SureTax data structures with tests for NewSureTaxRequest
This commit is contained in:
@@ -238,7 +238,7 @@ type CGRConfig struct {
|
||||
MailerAuthUser string // Authenticate to email server using this user
|
||||
MailerAuthPass string // Authenticate to email server with this password
|
||||
MailerFromAddr string // From address used when sending emails out
|
||||
SureTax *SureTaxCfg // Load here SureTax configuration, as pointer so we can have runtime reloads in the future
|
||||
SureTaxCfg *SureTaxCfg // Load here SureTax configuration, as pointer so we can have runtime reloads in the future
|
||||
DataFolderPath string // Path towards data folder, for tests internal usage, not loading out of .json options
|
||||
ConfigReloads map[string]chan struct{} // Signals to specific entities that a config reload should occur
|
||||
// Cache defaults loaded from json and needing clones
|
||||
@@ -804,15 +804,33 @@ func (self *CGRConfig) loadFromJsonCfg(jsnCfg *CgrJsonCfg) error {
|
||||
}
|
||||
|
||||
if jsnSureTaxCfg != nil {
|
||||
self.SureTax = new(SureTaxCfg) // Reset previous values
|
||||
self.SureTaxCfg = new(SureTaxCfg) // Reset previous values
|
||||
if jsnSureTaxCfg.Url != nil {
|
||||
self.SureTax.Url = *jsnSureTaxCfg.Url
|
||||
self.SureTaxCfg.Url = *jsnSureTaxCfg.Url
|
||||
}
|
||||
if jsnSureTaxCfg.Client_number != nil {
|
||||
self.SureTax.ClientNumber = *jsnSureTaxCfg.Client_number
|
||||
self.SureTaxCfg.ClientNumber = *jsnSureTaxCfg.Client_number
|
||||
}
|
||||
if jsnSureTaxCfg.Validation_key != nil {
|
||||
self.SureTax.ValidationKey = *jsnSureTaxCfg.Validation_key
|
||||
self.SureTaxCfg.ValidationKey = *jsnSureTaxCfg.Validation_key
|
||||
}
|
||||
if jsnSureTaxCfg.Timezone != nil {
|
||||
if self.SureTaxCfg.Timezone, err = time.LoadLocation(*jsnSureTaxCfg.Timezone); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if jsnSureTaxCfg.Include_local_cost != nil {
|
||||
self.SureTaxCfg.IncludeLocalCost = *jsnSureTaxCfg.Include_local_cost
|
||||
}
|
||||
if jsnSureTaxCfg.Origination_number != nil {
|
||||
if self.SureTaxCfg.OriginationNumber, err = utils.ParseRSRFields(*jsnSureTaxCfg.Origination_number, utils.INFIELD_SEP); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if jsnSureTaxCfg.Termination_number != nil {
|
||||
if self.SureTaxCfg.TerminationNumber, err = utils.ParseRSRFields(*jsnSureTaxCfg.Termination_number, utils.INFIELD_SEP); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -283,6 +283,10 @@ const CGRATES_CFG_JSON = `
|
||||
"url": "", // API url
|
||||
"client_number": "", // client number, provided by SureTax
|
||||
"validation_key": "", // validation key provided by SureTax
|
||||
"timezone": "Local", // convert the time of the events to this timezone before sending request out <UTC|Local|$IANA_TZ_DB>
|
||||
"include_local_cost": false, // sum local calculated cost with tax one in final cost
|
||||
"origination_number": "Subject", // template extracting origination number out of StoredCdr
|
||||
"termination_number": "Destination", // template extracting termination number out of StoredCdr
|
||||
},
|
||||
|
||||
}`
|
||||
|
||||
@@ -459,9 +459,13 @@ func TestDfMailerJsonCfg(t *testing.T) {
|
||||
|
||||
func TestDfSureTaxJsonCfg(t *testing.T) {
|
||||
eCfg := &SureTaxJsonCfg{
|
||||
Url: utils.StringPointer(""),
|
||||
Client_number: utils.StringPointer(""),
|
||||
Validation_key: utils.StringPointer(""),
|
||||
Url: utils.StringPointer(""),
|
||||
Client_number: utils.StringPointer(""),
|
||||
Validation_key: utils.StringPointer(""),
|
||||
Timezone: utils.StringPointer("Local"),
|
||||
Include_local_cost: utils.BoolPointer(false),
|
||||
Origination_number: utils.StringPointer("Subject"),
|
||||
Termination_number: utils.StringPointer("Destination"),
|
||||
}
|
||||
if cfg, err := dfCgrJsonCfg.SureTaxJsonCfg(); err != nil {
|
||||
t.Error(err)
|
||||
|
||||
@@ -19,6 +19,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
package config
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/cgrates/cgrates/utils"
|
||||
)
|
||||
|
||||
@@ -31,7 +33,11 @@ type CdrReplicationCfg struct {
|
||||
}
|
||||
|
||||
type SureTaxCfg struct {
|
||||
Url string
|
||||
ClientNumber string
|
||||
ValidationKey string
|
||||
Url string
|
||||
ClientNumber string
|
||||
ValidationKey string
|
||||
Timezone *time.Location // Convert the time of the events to this timezone before sending request out
|
||||
IncludeLocalCost bool
|
||||
OriginationNumber utils.RSRFields // Concatenate all of them to get value
|
||||
TerminationNumber utils.RSRFields // Concatenate all of them to get value
|
||||
}
|
||||
|
||||
@@ -256,7 +256,11 @@ type MailerJsonCfg struct {
|
||||
|
||||
// SureTax config section
|
||||
type SureTaxJsonCfg struct {
|
||||
Url *string // API url
|
||||
Client_number *string // client number provided by SureTax
|
||||
Validation_key *string // validation key provided by SureTax
|
||||
Url *string // API url
|
||||
Client_number *string // client number provided by SureTax
|
||||
Validation_key *string // validation key provided by SureTax
|
||||
Timezone *string // convert the time of the events to this timezone before sending request out
|
||||
Include_local_cost *bool // sum local calculated cost with tax one
|
||||
Origination_number *string // template extracting origination number out of StoredCdr
|
||||
Termination_number *string // template extracting origination number out of StoredCdr
|
||||
}
|
||||
|
||||
@@ -135,6 +135,9 @@ func (storedCdr *StoredCdr) FormatUsage(layout string) string {
|
||||
|
||||
// Used to retrieve fields as string, primary fields are const labeled
|
||||
func (storedCdr *StoredCdr) FieldAsString(rsrFld *utils.RSRField) string {
|
||||
if rsrFld.IsStatic() { // Static values do not care about headers
|
||||
return rsrFld.ParseValue("")
|
||||
}
|
||||
switch rsrFld.Id {
|
||||
case utils.CGRID:
|
||||
return rsrFld.ParseValue(storedCdr.CgrId)
|
||||
@@ -189,6 +192,15 @@ func (storedCdr *StoredCdr) FieldAsString(rsrFld *utils.RSRField) string {
|
||||
}
|
||||
}
|
||||
|
||||
// concatenates values of multiple fields defined in template, used eg in CDR templates
|
||||
func (storedCdr *StoredCdr) FieldsAsString(rsrFlds utils.RSRFields) string {
|
||||
var fldVal string
|
||||
for _, rsrFld := range rsrFlds {
|
||||
fldVal += storedCdr.FieldAsString(rsrFld)
|
||||
}
|
||||
return fldVal
|
||||
}
|
||||
|
||||
func (storedCdr *StoredCdr) PassesFieldFilter(fieldFilter *utils.RSRField) (bool, string) {
|
||||
if fieldFilter == nil {
|
||||
return true, ""
|
||||
|
||||
@@ -124,6 +124,20 @@ func TestFieldAsString(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestFieldsAsString(t *testing.T) {
|
||||
cdr := StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf",
|
||||
CdrHost: "192.168.1.1", CdrSource: "test", ReqType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org",
|
||||
Category: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC),
|
||||
AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), MediationRunId: utils.DEFAULT_RUNID,
|
||||
Usage: time.Duration(10) * time.Second, Pdd: time.Duration(5) * time.Second, Supplier: "SUPPL1", ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"},
|
||||
Cost: 1.01, RatedAccount: "dan", RatedSubject: "dans",
|
||||
}
|
||||
eVal := "call_from_1001"
|
||||
if val := cdr.FieldsAsString(utils.ParseRSRFieldsMustCompile("Category;^_from_;Account", utils.INFIELD_SEP)); val != eVal {
|
||||
t.Errorf("Expecting : %s, received: %s", eVal, val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPassesFieldFilter(t *testing.T) {
|
||||
cdr := &StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf",
|
||||
CdrHost: "192.168.1.1", CdrSource: "test", ReqType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org",
|
||||
|
||||
154
engine/suretax.go
Normal file
154
engine/suretax.go
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
Rating system designed to be used in VoIP Carriers World
|
||||
Copyright (C) 2012-2015 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 <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
package engine
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/cgrates/cgrates/utils"
|
||||
)
|
||||
|
||||
// Part of SureTax Request
|
||||
type STRequestItem struct {
|
||||
LineNumber string // Used to identify an item within the request. If no value is provided, requests are numbered sequentially. Max Len: 40
|
||||
InvoiceNumber string // Used for tax aggregation by Invoice. Must be alphanumeric. Max Len: 40
|
||||
CustomerNumber string // Used for tax aggregation by Customer. Must be alphanumeric. Max Len: 40
|
||||
OrigNumber string // Required when using Tax Situs Rule 01 or 03. Format: NPANXXNNNN
|
||||
TermNumber string // Required when using Tax Situs Rule 01. Format: NPANXXNNNN
|
||||
BillToNumber string // Required when using Tax Situs Rule 01 or 02. Format: NPANXXNNNN
|
||||
Zipcode string // Required when using Tax Situs Rule 04, 05, or 14.
|
||||
Plus4 string // Zip code extension in format: 9999 (not applicable for Tax Situs Rule 14)
|
||||
P2PZipcode string // Secondary zip code in format: 99999 (US or US territory) or X9X9X9 (Canadian)
|
||||
P2PPlus4 string // Secondary zip code extension in format: 99999 (US or US territory) or X9X9X9 (Canadian)
|
||||
TransDate string // Required. Date of transaction. Valid date formats include: MM/DD/YYYY, MM-DD-YYYY, YYYY-MM-DDTHH:MM:SS
|
||||
Revenue float64 // Required. Format: $$$$$$$$$.CCCC. For Negative charges, the first position should have a minus ‘-‘indicator.
|
||||
Units int64 // Required. Units representing number of “lines” or unique charges contained within the revenue. This value is essentially a multiplier on unit-based fees (e.g. E911 fees). Format: 99999. Default should be 1 (one unit).
|
||||
UnitType string // Required. 00 – Default / Number of unique access lines.
|
||||
Seconds int64 // Required. Duration of call in seconds. Format 99999. Default should be 1.
|
||||
TaxIncludedCode string // Required. Values: 0 – Default (No Tax Included) 1 – Tax Included in Revenue
|
||||
TaxSitusRule string // Required.
|
||||
TransTypeCode string // Required. Transaction Type Indicator.
|
||||
SalesTypeCode string // Required. Values: R – Residential customer (default) B – Business customer I – Industrial customer L – Lifeline customer
|
||||
RegulatoryCode string // Required. Provider Type.
|
||||
TaxExemptionCodeList []string // Required. Tax Exemption to be applied to this item only.
|
||||
}
|
||||
|
||||
// Init a new request to be sent out to SureTax
|
||||
func NewSureTaxRequest(clientNumber, validationKey string, timezone *time.Location, originationNrTpl, terminationNrTpl utils.RSRFields, cdr *StoredCdr) (*SureTaxRequest, error) {
|
||||
if clientNumber == "" {
|
||||
return nil, utils.NewErrMandatoryIeMissing("ClientNumber")
|
||||
}
|
||||
if validationKey == "" {
|
||||
return nil, utils.NewErrMandatoryIeMissing("ValidationKey")
|
||||
}
|
||||
aTime := cdr.AnswerTime.In(timezone)
|
||||
stReq := &SureTaxRequest{ClientNumber: clientNumber,
|
||||
ValidationKey: validationKey,
|
||||
DataYear: strconv.Itoa(aTime.Year()),
|
||||
DataMonth: strconv.Itoa(int(aTime.Month())),
|
||||
TotalRevenue: utils.Round(cdr.Cost, 4, utils.ROUNDING_MIDDLE),
|
||||
ReturnFileCode: "0",
|
||||
ClientTracking: cdr.CgrId,
|
||||
ResponseGroup: "03",
|
||||
ResponseType: "",
|
||||
ItemList: []*STRequestItem{
|
||||
&STRequestItem{
|
||||
OrigNumber: cdr.FieldsAsString(originationNrTpl),
|
||||
TermNumber: cdr.FieldsAsString(terminationNrTpl),
|
||||
BillToNumber: cdr.FieldsAsString(originationNrTpl),
|
||||
TransDate: aTime.Format("2006-01-02T15:04:05"),
|
||||
Revenue: utils.Round(cdr.Cost, 4, utils.ROUNDING_MIDDLE),
|
||||
Units: 1,
|
||||
UnitType: "00",
|
||||
Seconds: int64(utils.Round(cdr.Usage.Seconds(), 0, utils.ROUNDING_MIDDLE)),
|
||||
TaxIncludedCode: "0",
|
||||
TaxSitusRule: "1",
|
||||
TransTypeCode: "010101",
|
||||
SalesTypeCode: "R",
|
||||
RegulatoryCode: "01",
|
||||
TaxExemptionCodeList: []string{"00"},
|
||||
},
|
||||
},
|
||||
}
|
||||
return stReq, nil
|
||||
}
|
||||
|
||||
// SureTax Request type
|
||||
type SureTaxRequest 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
|
||||
DataYear string // Required. YYYY – Year to use for tax calculation purposes
|
||||
DataMonth string // Required. MM – Month to use for tax calculation purposes. Leading zero is preferred.
|
||||
TotalRevenue float64 // Required. Format: $$$$$$$$$.CCCC. For Negative charges, the first position should have a minus ‘-‘ indicator.
|
||||
ReturnFileCode string // Required. 0 – Default.Q – Quote purposes – taxes are computed and returned in the response message for generating quotes.
|
||||
ClientTracking string // Field for client transaction tracking. This value will be provided in the response data. Value for this field is not required, but preferred. Max Len: 100
|
||||
IndustryExemption string // Reserved for future use.
|
||||
ResponseGroup string // Required. Determines how taxes are grouped for the response.
|
||||
ResponseType string // Required. Determines the granularity of taxes and (optionally) the decimal precision for the tax calculations and amounts in the response.
|
||||
ItemList []*STRequestItem // List of Item records
|
||||
}
|
||||
|
||||
// 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
|
||||
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
|
||||
TransId int // Transaction ID – provided by SureTax
|
||||
GroupList []*STGroup // contains one-to-many Groups
|
||||
}
|
||||
|
||||
// Part of the SureTax Response
|
||||
type STItemMessage struct {
|
||||
LineNumber string // value corresponding to the line number in the web request
|
||||
ResponseCode string // a value in the range 9100-9400
|
||||
Message string // the error message corresponding to the ResponseCode
|
||||
}
|
||||
|
||||
// Part of the SureTax Response
|
||||
type STGroup struct {
|
||||
StateCode string // Tax State
|
||||
InvoiceNumber string // Invoice Number
|
||||
CustomerNumber string // Customer number
|
||||
TaxList []*STTaxItem // contains one-to-many Tax Items
|
||||
}
|
||||
|
||||
// Part of the SureTax Response
|
||||
type STTaxItem struct {
|
||||
TaxTypeCode string // Tax Type Code
|
||||
TaxTypeDesc string // Tax Type Description
|
||||
TaxAmount float64 // Tax Amount
|
||||
}
|
||||
76
engine/suretax_test.go
Normal file
76
engine/suretax_test.go
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
Real-time Charging System for Telecom & ISP environments
|
||||
Copyright (C) 2012-2015 ITsysCOM GmbH
|
||||
|
||||
This program is free software: you can Storagetribute 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 WITH*out 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 <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
package engine
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cgrates/cgrates/config"
|
||||
"github.com/cgrates/cgrates/utils"
|
||||
)
|
||||
|
||||
func TestNewSureTaxRequest(t *testing.T) {
|
||||
storedCdr := &StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE,
|
||||
AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: utils.UNIT_TEST, ReqType: utils.META_RATED, Direction: "*out",
|
||||
Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", Supplier: "SUPPL1",
|
||||
SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), MediationRunId: utils.DEFAULT_RUNID,
|
||||
Usage: time.Duration(12) * time.Second, Pdd: time.Duration(7) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, RatedAccount: "dan", RatedSubject: "dans", Rated: true,
|
||||
}
|
||||
cfg, _ := config.NewDefaultCGRConfig()
|
||||
cfg.SureTaxCfg.ClientNumber = "000000000"
|
||||
cfg.SureTaxCfg.ValidationKey = "19491161-F004-4F44-BDB3-E976D6739A64"
|
||||
cfg.SureTaxCfg.Timezone = time.UTC
|
||||
eSureTaxRequest := &SureTaxRequest{
|
||||
ClientNumber: cfg.SureTaxCfg.ClientNumber,
|
||||
ValidationKey: cfg.SureTaxCfg.ValidationKey,
|
||||
DataYear: "2013",
|
||||
DataMonth: "11",
|
||||
TotalRevenue: 1.01,
|
||||
ReturnFileCode: "0",
|
||||
ClientTracking: storedCdr.CgrId,
|
||||
ResponseGroup: "03",
|
||||
ResponseType: "",
|
||||
ItemList: []*STRequestItem{
|
||||
&STRequestItem{
|
||||
OrigNumber: "1001",
|
||||
TermNumber: "1002",
|
||||
BillToNumber: "1001",
|
||||
TransDate: "2013-11-07T08:42:26",
|
||||
Revenue: 1.01,
|
||||
Units: 1,
|
||||
UnitType: "00",
|
||||
Seconds: 12,
|
||||
TaxIncludedCode: "0",
|
||||
TaxSitusRule: "1",
|
||||
TransTypeCode: "010101",
|
||||
SalesTypeCode: "R",
|
||||
RegulatoryCode: "01",
|
||||
TaxExemptionCodeList: []string{"00"},
|
||||
},
|
||||
},
|
||||
}
|
||||
if stReq, err := NewSureTaxRequest(cfg.SureTaxCfg.ClientNumber, cfg.SureTaxCfg.ValidationKey, cfg.SureTaxCfg.Timezone, cfg.SureTaxCfg.OriginationNumber,
|
||||
cfg.SureTaxCfg.TerminationNumber, storedCdr); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(eSureTaxRequest, stReq) {
|
||||
t.Errorf("Expecting: %+v, received: %+v", eSureTaxRequest.ItemList[0], stReq.ItemList[0])
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user