diff --git a/account.go b/account.go new file mode 100644 index 0000000..cc3d504 --- /dev/null +++ b/account.go @@ -0,0 +1,198 @@ +package quickbooks + +import ( + "bytes" + "encoding/json" + "net/http" + "net/url" + "strconv" +) + +const ( + BankAccountType = "Bank" + OtherCurrentAssetAccountType = "Other Current Asset" + FixedAssetAccountType = "Fixed Asset" + OtherAssetAccountType = "Other Asset" + AccountsReceivableAccountType = "Accounts Receivable" + EquityAccountType = "Equity" + ExpenseAccountType = "Expense" + OtherExpenseAccountType = "Other Expense" + CostOfGoodsSoldAccountType = "Cost of Goods Sold" + AccountsPayableAccountType = "Accounts Payable" + CreditCardAccountType = "Credit Card" + LongTermLiabilityAccountType = "Long Term Liability" + OtherCurrentLiabilityAccountType = "Other Current Liability" + IncomeAccountType = "Income" + OtherIncomeAccountType = "Other Income" +) + +type Account struct { + ID string `json:"Id,omitempty"` + Name string `json:",omitempty"` + SyncToken string `json:",omitempty"` + AcctNum string `json:",omitempty"` + CurrencyRef ReferenceType `json:",omitempty"` + ParentRef ReferenceType `json:",omitempty"` + Description string `json:",omitempty"` + Active bool `json:",omitempty"` + MetaData MetaData `json:",omitempty"` + SubAccount bool `json:",omitempty"` + Classification string `json:",omitempty"` + FullyQualifiedName string `json:",omitempty"` + TxnLocationType string `json:",omitempty"` + AccountType string `json:",omitempty"` + CurrentBalanceWithSubAccounts json.Number `json:",omitempty"` + AccountAlias string `json:",omitempty"` + TaxCodeRef ReferenceType `json:",omitempty"` + AccountSubType string `json:",omitempty"` + CurrentBalance json.Number `json:",omitempty"` +} + +// CreateAccount creates the account +func (c *Client) CreateAccount(account *Account) (*Account, error) { + var u, err = url.Parse(string(c.Endpoint)) + if err != nil { + return nil, err + } + u.Path = "/v3/company/" + c.RealmID + "/account" + var v = url.Values{} + v.Add("minorversion", minorVersion) + u.RawQuery = v.Encode() + var j []byte + j, err = json.Marshal(account) + if err != nil { + return nil, err + } + var req *http.Request + req, err = http.NewRequest("POST", u.String(), bytes.NewBuffer(j)) + if err != nil { + return nil, err + } + req.Header.Add("Content-Type", "application/json") + req.Header.Add("Accept", "application/json") + var res *http.Response + res, err = c.Client.Do(req) + if err != nil { + return nil, err + } + defer res.Body.Close() + + if res.StatusCode != http.StatusOK { + return nil, parseFailure(res) + } + + var r struct { + Account Account + Time Date + } + err = json.NewDecoder(res.Body).Decode(&r) + return &r.Account, err +} + +// QueryAccount gets the account +func (c *Client) QueryAccount(selectStatement string) ([]Account, error) { + var r struct { + QueryResponse struct { + Account []Account + StartPosition int + MaxResults int + } + } + err := c.query(selectStatement, &r) + if err != nil { + return nil, err + } + + if r.QueryResponse.Account == nil { + r.QueryResponse.Account = make([]Account, 0) + } + return r.QueryResponse.Account, nil +} + +// GetAccounts gets the account +func (c *Client) GetAccounts(startpos int, pagesize int) ([]Account, error) { + q := "SELECT * FROM Account ORDERBY Id STARTPOSITION " + + strconv.Itoa(startpos) + " MAXRESULTS " + strconv.Itoa(pagesize) + return c.QueryAccount(q) +} + +// GetAccountByID returns an account with a given ID. +func (c *Client) GetAccountByID(id string) (*Account, error) { + var u, err = url.Parse(string(c.Endpoint)) + if err != nil { + return nil, err + } + u.Path = "/v3/company/" + c.RealmID + "/account/" + id + var v = url.Values{} + v.Add("minorversion", minorVersion) + u.RawQuery = v.Encode() + var req *http.Request + req, err = http.NewRequest("GET", u.String(), nil) + if err != nil { + return nil, err + } + req.Header.Add("Accept", "application/json") + var res *http.Response + res, err = c.Client.Do(req) + if err != nil { + return nil, err + } + defer res.Body.Close() + if res.StatusCode != http.StatusOK { + return nil, parseFailure(res) + } + var r struct { + Account Account + Time Date + } + err = json.NewDecoder(res.Body).Decode(&r) + return &r.Account, err +} + +// UpdateAccount updates the account +func (c *Client) UpdateAccount(account *Account) (*Account, error) { + var u, err = url.Parse(string(c.Endpoint)) + if err != nil { + return nil, err + } + u.Path = "/v3/company/" + c.RealmID + "/account" + var v = url.Values{} + v.Add("minorversion", minorVersion) + u.RawQuery = v.Encode() + var d = struct { + *Account + Sparse bool `json:"sparse"` + }{ + Account: account, + Sparse: true, + } + var j []byte + j, err = json.Marshal(d) + if err != nil { + return nil, err + } + var req *http.Request + req, err = http.NewRequest("POST", u.String(), bytes.NewBuffer(j)) + if err != nil { + return nil, err + } + req.Header.Add("Content-Type", "application/json") + req.Header.Add("Accept", "application/json") + var res *http.Response + res, err = c.Client.Do(req) + if err != nil { + return nil, err + } + defer res.Body.Close() + + if res.StatusCode != http.StatusOK { + return nil, parseFailure(res) + } + + var r struct { + Account Account + Time Date + } + err = json.NewDecoder(res.Body).Decode(&r) + return &r.Account, err +} diff --git a/account_test.go b/account_test.go new file mode 100644 index 0000000..1faf164 --- /dev/null +++ b/account_test.go @@ -0,0 +1,40 @@ +package quickbooks + +import ( + "encoding/json" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "io/ioutil" + "os" + "testing" +) + +func TestAccount(t *testing.T) { + jsonFile, err := os.Open("data/testing/account.json") + require.NoError(t, err) + defer jsonFile.Close() + + byteValue, _ := ioutil.ReadAll(jsonFile) + require.NoError(t, err) + + var r struct { + Account Account + Time Date + } + err = json.Unmarshal(byteValue, &r) + require.NoError(t, err) + + assert.Equal(t, "MyJobs", r.Account.FullyQualifiedName) + assert.Equal(t, "MyJobs", r.Account.Name) + assert.Equal(t, "Asset", r.Account.Classification) + assert.Equal(t, "AccountsReceivable", r.Account.AccountSubType) + assert.Equal(t, json.Number("0"), r.Account.CurrentBalanceWithSubAccounts) + assert.Equal(t, "2014-12-31T09:29:05-08:00", r.Account.MetaData.CreateTime.String()) + assert.Equal(t, "2014-12-31T09:29:05-08:00", r.Account.MetaData.LastUpdatedTime.String()) + assert.Equal(t, AccountsReceivableAccountType, r.Account.AccountType) + assert.Equal(t, json.Number("0"), r.Account.CurrentBalance) + assert.True(t, r.Account.Active) + assert.Equal(t, "0", r.Account.SyncToken) + assert.Equal(t, "94", r.Account.ID) + assert.False(t, r.Account.SubAccount) +} diff --git a/data/testing/account.json b/data/testing/account.json new file mode 100644 index 0000000..5f2e1da --- /dev/null +++ b/data/testing/account.json @@ -0,0 +1,26 @@ +{ + "Account": { + "FullyQualifiedName": "MyJobs", + "domain": "QBO", + "Name": "MyJobs", + "Classification": "Asset", + "AccountSubType": "AccountsReceivable", + "CurrencyRef": { + "name": "United States Dollar", + "value": "USD" + }, + "CurrentBalanceWithSubAccounts": 0, + "sparse": false, + "MetaData": { + "CreateTime": "2014-12-31T09:29:05-08:00", + "LastUpdatedTime": "2014-12-31T09:29:05-08:00" + }, + "AccountType": "Accounts Receivable", + "CurrentBalance": 0, + "Active": true, + "SyncToken": "0", + "Id": "94", + "SubAccount": false + }, + "time": "2014-12-31T09:29:05.717-08:00" +}