xeroizer 2.18.1 → 3.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +199 -223
- data/lib/xeroizer/connection.rb +49 -0
- data/lib/xeroizer/exceptions.rb +2 -0
- data/lib/xeroizer/generic_application.rb +12 -5
- data/lib/xeroizer/http.rb +5 -78
- data/lib/xeroizer/http_response.rb +157 -0
- data/lib/xeroizer/models/bank_account.rb +1 -0
- data/lib/xeroizer/models/bank_transaction.rb +1 -0
- data/lib/xeroizer/models/batch_payment.rb +27 -0
- data/lib/xeroizer/models/branding_theme.rb +49 -9
- data/lib/xeroizer/models/contact.rb +10 -4
- data/lib/xeroizer/models/contact_group.rb +45 -0
- data/lib/xeroizer/models/credit_note.rb +23 -22
- data/lib/xeroizer/models/from_bank_account.rb +1 -0
- data/lib/xeroizer/models/history_record.rb +72 -0
- data/lib/xeroizer/models/invoice.rb +14 -3
- data/lib/xeroizer/models/line_item.rb +17 -5
- data/lib/xeroizer/models/manual_journal.rb +2 -1
- data/lib/xeroizer/models/option.rb +1 -1
- data/lib/xeroizer/models/organisation.rb +2 -0
- data/lib/xeroizer/models/payment_service.rb +22 -0
- data/lib/xeroizer/models/payroll/address.rb +53 -0
- data/lib/xeroizer/models/payroll/bank_account.rb +18 -6
- data/lib/xeroizer/models/payroll/benefit_line.rb +26 -0
- data/lib/xeroizer/models/payroll/benefit_type.rb +45 -0
- data/lib/xeroizer/models/payroll/deduction_line.rb +32 -0
- data/lib/xeroizer/models/payroll/deduction_type.rb +49 -0
- data/lib/xeroizer/models/payroll/earnings_line.rb +39 -0
- data/lib/xeroizer/models/payroll/earnings_type.rb +53 -0
- data/lib/xeroizer/models/payroll/employee.rb +30 -8
- data/lib/xeroizer/models/payroll/leave_application.rb +27 -0
- data/lib/xeroizer/models/payroll/leave_line.rb +30 -0
- data/lib/xeroizer/models/payroll/leave_period.rb +15 -0
- data/lib/xeroizer/models/payroll/pay_items.rb +22 -0
- data/lib/xeroizer/models/payroll/pay_run.rb +33 -0
- data/lib/xeroizer/models/payroll/pay_schedule.rb +40 -0
- data/lib/xeroizer/models/payroll/pay_template.rb +24 -0
- data/lib/xeroizer/models/payroll/payment_method.rb +24 -0
- data/lib/xeroizer/models/payroll/paystub.rb +44 -0
- data/lib/xeroizer/models/payroll/reimbursement_line.rb +21 -0
- data/lib/xeroizer/models/payroll/reimbursement_type.rb +22 -0
- data/lib/xeroizer/models/payroll/salary_and_wage.rb +29 -0
- data/lib/xeroizer/models/payroll/super_line.rb +40 -0
- data/lib/xeroizer/models/payroll/tax_declaration.rb +50 -0
- data/lib/xeroizer/models/payroll/time_off_line.rb +20 -0
- data/lib/xeroizer/models/payroll/time_off_type.rb +32 -0
- data/lib/xeroizer/models/payroll/work_location.rb +25 -0
- data/lib/xeroizer/models/prepayment.rb +1 -0
- data/lib/xeroizer/models/purchase_order.rb +6 -6
- data/lib/xeroizer/models/quote.rb +76 -0
- data/lib/xeroizer/models/schedule.rb +1 -0
- data/lib/xeroizer/models/tax_component.rb +1 -0
- data/lib/xeroizer/models/to_bank_account.rb +1 -0
- data/lib/xeroizer/oauth.rb +12 -1
- data/lib/xeroizer/oauth2.rb +82 -0
- data/lib/xeroizer/oauth2_application.rb +49 -0
- data/lib/xeroizer/payroll_application.rb +8 -3
- data/lib/xeroizer/record/base.rb +1 -1
- data/lib/xeroizer/record/base_model.rb +1 -1
- data/lib/xeroizer/record/base_model_http_proxy.rb +4 -0
- data/lib/xeroizer/record/model_definition_helper.rb +1 -1
- data/lib/xeroizer/record/payroll_base.rb +4 -0
- data/lib/xeroizer/record/record_association_helper.rb +4 -4
- data/lib/xeroizer/record/validators/associated_validator.rb +1 -0
- data/lib/xeroizer/record/xml_helper.rb +18 -18
- data/lib/xeroizer/report/aged_receivables_by_contact.rb +1 -1
- data/lib/xeroizer/report/cell_xml_helper.rb +13 -13
- data/lib/xeroizer/response.rb +22 -17
- data/lib/xeroizer/version.rb +1 -1
- data/lib/xeroizer.rb +33 -4
- data/test/acceptance/about_creating_bank_transactions_test.rb +80 -82
- data/test/acceptance/about_creating_prepayment_test.rb +25 -30
- data/test/acceptance/about_fetching_bank_transactions_test.rb +12 -12
- data/test/acceptance/about_online_invoice_test.rb +6 -10
- data/test/acceptance/acceptance_test.rb +28 -26
- data/test/acceptance/bank_transfer_test.rb +12 -17
- data/test/acceptance/bulk_operations_test.rb +18 -16
- data/test/acceptance/connections_test.rb +11 -0
- data/test/stub_responses/bad_request.json +6 -0
- data/test/stub_responses/connections.json +16 -0
- data/test/stub_responses/expired_oauth2_token.json +6 -0
- data/test/stub_responses/generic_response_error.json +6 -0
- data/test/stub_responses/invalid_oauth2_request_token.json +6 -0
- data/test/stub_responses/invalid_tenant_header.json +6 -0
- data/test/stub_responses/object_not_found.json +6 -0
- data/test/stub_responses/organisations.xml +10 -0
- data/test/stub_responses/payment_service.xml +15 -0
- data/test/test_helper.rb +16 -11
- data/test/unit/generic_application_test.rb +21 -10
- data/test/unit/http_test.rb +284 -10
- data/test/unit/models/address_test.rb +2 -2
- data/test/unit/models/bank_transaction_model_parsing_test.rb +2 -2
- data/test/unit/models/bank_transaction_test.rb +1 -1
- data/test/unit/models/bank_transaction_validation_test.rb +1 -1
- data/test/unit/models/contact_test.rb +2 -2
- data/test/unit/models/credit_note_test.rb +8 -8
- data/test/unit/models/employee_test.rb +4 -4
- data/test/unit/models/invoice_test.rb +12 -12
- data/test/unit/models/journal_line_test.rb +6 -6
- data/test/unit/models/journal_test.rb +4 -4
- data/test/unit/models/line_item_sum_test.rb +1 -1
- data/test/unit/models/line_item_test.rb +26 -28
- data/test/unit/models/manual_journal_test.rb +3 -3
- data/test/unit/models/organisation_test.rb +16 -2
- data/test/unit/models/payment_service_test.rb +29 -0
- data/test/unit/models/phone_test.rb +7 -7
- data/test/unit/models/prepayment_test.rb +4 -4
- data/test/unit/models/repeating_invoice_test.rb +3 -3
- data/test/unit/models/tax_rate_test.rb +2 -2
- data/test/unit/oauth2_test.rb +171 -0
- data/test/unit/oauth_config_test.rb +1 -1
- data/test/unit/record/base_model_test.rb +13 -13
- data/test/unit/record/base_test.rb +15 -4
- data/test/unit/record/block_validator_test.rb +1 -1
- data/test/unit/record/connection_test.rb +60 -0
- data/test/unit/record/model_definition_test.rb +36 -36
- data/test/unit/record/parse_params_test.rb +2 -2
- data/test/unit/record/parse_where_hash_test.rb +13 -13
- data/test/unit/record/record_association_test.rb +14 -14
- data/test/unit/record/validators_test.rb +43 -43
- data/test/unit/record_definition_test.rb +7 -7
- data/test/unit/report_definition_test.rb +7 -7
- data/test/unit/report_test.rb +20 -20
- data/test/unit_test_helper.rb +16 -0
- metadata +100 -25
- data/lib/xeroizer/models/payroll/home_address.rb +0 -24
- data/lib/xeroizer/partner_application.rb +0 -51
- data/lib/xeroizer/private_application.rb +0 -25
- data/lib/xeroizer/public_application.rb +0 -21
- data/test/unit/http_tsl_12_upgrade_test.rb +0 -31
- data/test/unit/oauth_test.rb +0 -118
- data/test/unit/private_application_test.rb +0 -20
data/README.md
CHANGED
@@ -1,13 +1,20 @@
|
|
1
|
-
Xeroizer API Library
|
1
|
+
Xeroizer API Library
|
2
2
|
====================
|
3
3
|
|
4
|
-
**Homepage**: [http://waynerobinson.github.com/xeroizer](http://waynerobinson.github.com/xeroizer)
|
5
|
-
|
6
|
-
**
|
7
|
-
|
8
|
-
**
|
4
|
+
**Homepage**: [http://waynerobinson.github.com/xeroizer](http://waynerobinson.github.com/xeroizer)
|
5
|
+
|
6
|
+
**Git**: [git://github.com/waynerobinson/xeroizer.git](git://github.com/waynerobinson/xeroizer.git)
|
7
|
+
|
8
|
+
**Github**: [https://github.com/waynerobinson/xeroizer](https://github.com/waynerobinson/xeroizer)
|
9
|
+
|
10
|
+
**Author**: Wayne Robinson [http://www.wayne-robinson.com](http://www.wayne-robinson.com)
|
11
|
+
|
12
|
+
**Contributors**: See Contributors section below
|
13
|
+
|
9
14
|
**Copyright**: 2007-2013
|
10
|
-
|
15
|
+
|
16
|
+
**License**: MIT License
|
17
|
+
|
11
18
|
|
12
19
|
Introduction
|
13
20
|
------------
|
@@ -29,232 +36,184 @@ require 'rubygems'
|
|
29
36
|
require 'xeroizer'
|
30
37
|
|
31
38
|
# Create client (used to communicate with the API).
|
32
|
-
client = Xeroizer::
|
39
|
+
client = Xeroizer::OAuth2Application.new(YOUR_OAUTH2_CLIENT_ID, YOUR_OAUTH2_CLIENT_SECRET)
|
33
40
|
|
34
41
|
# Retrieve list of contacts (note: all communication must be made through the client).
|
35
42
|
contacts = client.Contact.all(:order => 'Name')
|
36
43
|
```
|
37
|
-
|
44
|
+
|
38
45
|
Authentication
|
39
46
|
--------------
|
40
47
|
|
41
|
-
Xero uses OAuth to authenticate API clients. The OAuth gem (with minor modification) by John Nunemaker ([http://github.com/jnunemaker/twitter](http://github.com/jnunemaker/twitter)) is used in this library. If you've used this before, things will all seem very familar.
|
42
|
-
|
43
|
-
There are three methods of authentication detailed below:
|
44
|
-
|
45
|
-
### All: Consumer Key/Secret
|
46
|
-
|
47
|
-
All methods of authentication require your OAuth consumer key and secret. This can be found for your application
|
48
|
-
in the API management console at [http://api.xero.com](http://api.xero.com).
|
49
|
-
|
50
|
-
### Public Applications
|
51
|
-
|
52
|
-
Public applications use a 3-legged authorisation process. A user will need to authorise your
|
53
|
-
application against each organisation that you want access to. Your application can have access
|
54
|
-
to many organisations at once by going through the authorisation process for each organisation.
|
55
|
-
|
56
|
-
The access token received will expire after 30 minutes. If you want access for longer you will need
|
57
|
-
the user to re-authorise your application.
|
58
|
-
|
59
|
-
Authentication occurs in 3 steps:
|
60
|
-
|
61
|
-
```ruby
|
62
|
-
client = Xeroizer::PublicApplication.new(YOUR_OAUTH_CONSUMER_KEY, YOUR_OAUTH_CONSUMER_SECRET)
|
63
|
-
|
64
|
-
# 1. Get a RequestToken from Xero. :oauth_callback is the URL the user will be redirected to
|
65
|
-
# after they have authenticated your application.
|
66
|
-
#
|
67
|
-
# Note: The callback URL's domain must match that listed for your application in http://api.xero.com
|
68
|
-
# otherwise the user will not be redirected and only be shown the authentication code.
|
69
|
-
request_token = client.request_token(:oauth_callback => 'http://yourapp.com/oauth/callback')
|
70
|
-
|
71
|
-
# 2. Redirect the user to the URL specified by the RequestToken.
|
72
|
-
#
|
73
|
-
# Note: example uses redirect_to method defined in Rails controllers.
|
74
|
-
redirect_to request_token.authorize_url
|
75
|
-
|
76
|
-
# 3. Exchange RequestToken for AccessToken.
|
77
|
-
# This access token will be used for all subsequent requests but it is stored within the client
|
78
|
-
# application so you don't have to record it.
|
79
|
-
#
|
80
|
-
# Note: This example assumes the callback URL is a Rails action.
|
81
|
-
client.authorize_from_request(request_token.token, request_token.secret, :oauth_verifier => params[:oauth_verifier])
|
82
|
-
```
|
83
|
-
|
84
|
-
You can now use the client to access the Xero API methods, e.g.
|
85
|
-
|
86
|
-
```ruby
|
87
|
-
contacts = client.Contact.all
|
88
|
-
```
|
89
|
-
|
90
48
|
#### Example Rails Controller
|
91
49
|
|
92
50
|
```ruby
|
93
51
|
class XeroSessionController < ApplicationController
|
94
52
|
|
95
53
|
before_filter :get_xero_client
|
96
|
-
|
54
|
+
|
97
55
|
public
|
98
|
-
|
56
|
+
|
99
57
|
def new
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
58
|
+
url = @xero_client.authorize_url(
|
59
|
+
# The URL's domain must match that listed for your application
|
60
|
+
# otherwise the user will see an invalid redirect_uri error
|
61
|
+
redirect_uri: YOUR_CALLBACK_URL,
|
62
|
+
# space separated, see all scopes at https://developer.xero.com/documentation/oauth2/scopes.
|
63
|
+
# note that `offline_access` is required to get a refresh token, otherwise the access only lasts for 30 mins and cannot be refreshed.
|
64
|
+
scope: "accounting.settings.read offline_access"
|
65
|
+
)
|
66
|
+
|
67
|
+
redirect_to url
|
105
68
|
end
|
106
|
-
|
69
|
+
|
107
70
|
def create
|
108
|
-
@xero_client.
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
71
|
+
token = @xero_client.authorize_from_code(
|
72
|
+
params[:code],
|
73
|
+
redirect_uri: YOUR_CALLBACK_URL
|
74
|
+
)
|
75
|
+
|
76
|
+
connections = @xero_client.current_connections
|
77
|
+
|
113
78
|
session[:xero_auth] = {
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
79
|
+
:access_token => token[:access_token],
|
80
|
+
:refresh_token => token[:refresh_token],
|
81
|
+
:tenant_id => connections[1][:tenant_id]
|
82
|
+
}
|
83
|
+
|
119
84
|
end
|
120
|
-
|
85
|
+
|
121
86
|
def destroy
|
122
87
|
session.data.delete(:xero_auth)
|
123
88
|
end
|
124
|
-
|
89
|
+
|
125
90
|
private
|
126
|
-
|
91
|
+
|
127
92
|
def get_xero_client
|
128
|
-
@xero_client = Xeroizer::
|
129
|
-
|
93
|
+
@xero_client = Xeroizer::OAuth2Application.new(
|
94
|
+
YOUR_OAUTH2_CLIENT_ID,
|
95
|
+
YOUR_OAUTH2_CLIENT_SECRET,
|
96
|
+
)
|
97
|
+
|
130
98
|
# Add AccessToken if authorised previously.
|
131
99
|
if session[:xero_auth]
|
132
|
-
@xero_client.
|
133
|
-
|
134
|
-
|
100
|
+
@xero_client.tenant_id = session[:xero_auth][:tenant_id]
|
101
|
+
|
102
|
+
@xero_client.authorize_from_access(session[:xero_auth][:acesss_token])
|
135
103
|
end
|
136
104
|
end
|
137
105
|
end
|
138
106
|
```
|
139
|
-
|
140
|
-
#### Storing AccessToken
|
141
107
|
|
142
|
-
|
143
|
-
tokens are only valid for 30 minutes and will raise a `Xeroizer::OAuth::TokenExpired` exception if you try to access
|
144
|
-
the API beyond the token's expiry time.
|
108
|
+
### OAuth2 Applications
|
145
109
|
|
146
|
-
|
110
|
+
For more details, checkout Xero's [documentation](https://developer.xero.com/documentation/oauth2/auth-flow)
|
147
111
|
|
112
|
+
1. Generate the authorization url and redirect the user to authenticate
|
148
113
|
```ruby
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
Private applications require a private RSA keypair which is used to sign each request to the API. You can
|
162
|
-
generate this keypair on Mac OSX or Linux with OpenSSL. For example:
|
163
|
-
|
164
|
-
openssl genrsa -out privatekey.pem 1024
|
165
|
-
openssl req -newkey rsa:1024 -x509 -key privatekey.pem -out publickey.cer -days 365
|
166
|
-
openssl pkcs12 -export -out public_privatekey.pfx -inkey privatekey.pem -in publickey.cer
|
114
|
+
client = Xeroizer::OAuth2Application.new(
|
115
|
+
YOUR_OAUTH2_CLIENT_ID,
|
116
|
+
YOUR_OAUTH2_CLIENT_SECRET,
|
117
|
+
)
|
118
|
+
url = client.authorize_url(
|
119
|
+
# The URL's domain must match that listed for your application
|
120
|
+
# otherwise the user will see an invalid redirect_uri error
|
121
|
+
redirect_uri: YOUR_CALLBACK_URL,
|
122
|
+
# space separated, see all scopes at https://developer.xero.com/documentation/oauth2/scopes.
|
123
|
+
# note that `offline_access` is required to get a refresh token, otherwise the access only lasts for 30 mins and cannot be refreshed.
|
124
|
+
scope: "accounting.settings.read offline_access"
|
125
|
+
)
|
167
126
|
|
168
|
-
|
127
|
+
# Rails as an example
|
128
|
+
redirect_to url
|
129
|
+
```
|
169
130
|
|
170
|
-
|
131
|
+
2. In the callback route, use the provided code to retrieve an access token.
|
171
132
|
|
172
133
|
```ruby
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
134
|
+
token = client.authorize_from_code(
|
135
|
+
params[:code],
|
136
|
+
redirect_uri: YOUR_CALLBACK_URL
|
137
|
+
)
|
138
|
+
token.to_hash
|
139
|
+
# {
|
140
|
+
# "token_type"=>"Bearer",
|
141
|
+
# "scope"=>"accounting.transactions.read accounting.settings.read",
|
142
|
+
# :access_token=>"...",
|
143
|
+
# :refresh_token=>nil,
|
144
|
+
# :expires_at=>1615220292
|
145
|
+
# }
|
178
146
|
|
179
|
-
|
147
|
+
# Save the access_token, refresh_token...
|
148
|
+
```
|
180
149
|
|
181
|
-
|
150
|
+
3. Retrieve the tenant ids.
|
151
|
+
```ruby
|
152
|
+
connections = client.current_connections
|
153
|
+
# returns Xeroizer::Connection instances
|
182
154
|
|
183
|
-
|
184
|
-
access the partner application in a similar way to public applications.
|
155
|
+
# Save the tenant ids
|
185
156
|
|
186
|
-
|
157
|
+
```
|
187
158
|
|
159
|
+
4. Use access token and tenant ids to retrieve data.
|
188
160
|
```ruby
|
189
|
-
client = Xeroizer::
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
#
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
# 2. Redirect the user to the URL specified by the RequestToken.
|
203
|
-
#
|
204
|
-
# Note: example uses redirect_to method defined in Rails controllers.
|
205
|
-
redirect_to request_token.authorize_url
|
161
|
+
client = Xeroizer::OAuth2Application.new(
|
162
|
+
YOUR_OAUTH2_CLIENT_ID,
|
163
|
+
YOUR_OAUTH2_CLIENT_SECRET,
|
164
|
+
access_token: access_token,
|
165
|
+
tenant_id: tenant_id
|
166
|
+
)
|
167
|
+
# OR
|
168
|
+
client = Xeroizer::OAuth2Application.new(
|
169
|
+
YOUR_OAUTH2_CLIENT_ID,
|
170
|
+
YOUR_OAUTH2_CLIENT_SECRET,
|
171
|
+
tenant_id: tenant_id
|
172
|
+
).authorize_from_access(access_token)
|
206
173
|
|
207
|
-
#
|
208
|
-
|
209
|
-
# application so you don't have to record it.
|
210
|
-
#
|
211
|
-
# Note: This example assumes the callback URL is a Rails action.
|
212
|
-
client.authorize_from_request(request_token.token, request_token.secret, :oauth_verifier => params[:oauth_verifier])
|
174
|
+
# use the client
|
175
|
+
client.Organisation.first
|
213
176
|
```
|
214
177
|
|
215
|
-
|
216
|
-
|
217
|
-
AccessToken:
|
178
|
+
#### AccessToken Renewal
|
179
|
+
Renewal of an access token requires the refresh token generated for this organisation. To renew:
|
218
180
|
|
219
181
|
```ruby
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
> **`#expires_at`**: Time this AccessToken will expire (usually 30 minutes into the future).
|
228
|
-
> **`#authorization_expires_at`**: How long this organisation has authorised you to access their data (usually 10 years into the future).
|
182
|
+
client = Xeroizer::OAuth2Application.new(
|
183
|
+
YOUR_OAUTH2_CLIENT_ID,
|
184
|
+
YOUR_OAUTH2_CLIENT_SECRET,
|
185
|
+
access_token: access_token,
|
186
|
+
refresh_token: refresh_token,
|
187
|
+
tenant_id: tenant_id
|
188
|
+
)
|
229
189
|
|
230
|
-
|
190
|
+
client.renew_access_token
|
191
|
+
```
|
192
|
+
If you lose these details at any stage you can always reauthorise by redirecting the user back to the Xero OAuth gateway.
|
231
193
|
|
232
|
-
|
194
|
+
#### Custom Connections
|
195
|
+
Custom Connections are a paid-for option for private M2M applications. The generated token expires and needs recreating if expired.
|
233
196
|
|
234
197
|
```ruby
|
235
|
-
|
236
|
-
|
198
|
+
client = Xeroizer::OAuth2Application.new(
|
199
|
+
YOUR_OAUTH2_CLIENT_ID,
|
200
|
+
YOUR_OAUTH2_CLIENT_SECRET
|
201
|
+
)
|
237
202
|
|
238
|
-
|
239
|
-
client.renew_access_token(access_token, access_secret, session_handle)
|
203
|
+
token = client.authorize_from_client_credentials
|
240
204
|
```
|
205
|
+
You can check the status of the token with the `expires?` and `expired?` methods.
|
241
206
|
|
242
|
-
This will invalidate the previous token and refresh the `access_key` and `access_secret` as specified in the
|
243
|
-
initial authorisation process. You must always know the previous token's details to renew access to this
|
244
|
-
session.
|
245
|
-
|
246
|
-
If you lose these details at any stage you can always reauthorise by redirecting the user back to the Xero OAuth gateway.
|
247
207
|
|
248
208
|
Retrieving Data
|
249
209
|
---------------
|
250
210
|
|
251
211
|
Each of the below record types is implemented within this library. To allow for multiple access tokens to be used at the same
|
252
|
-
time in a single application, the model classes are accessed from the instance of
|
253
|
-
or PartnerApplication. All class-level operations occur on this singleton. For example:
|
212
|
+
time in a single application, the model classes are accessed from the instance of OAuth2Application. All class-level operations occur on this singleton. For example:
|
254
213
|
|
255
214
|
```ruby
|
256
|
-
xero = Xeroizer::
|
257
|
-
xero.authorize_from_access(session[:xero_auth][:access_token]
|
215
|
+
xero = Xeroizer::OAuth2Application.new(YOUR_OAUTH2_CLIENT_ID, YOUR_OAUTH2_CLIENT_SECRET, tenant_id: tenant_id)
|
216
|
+
xero.authorize_from_access(session[:xero_auth][:access_token])
|
258
217
|
|
259
218
|
contacts = xero.Contact.all(:order => 'Name')
|
260
219
|
|
@@ -264,10 +223,10 @@ saved = new_contact.save
|
|
264
223
|
|
265
224
|
### \#all([options])
|
266
225
|
|
267
|
-
Retrieves list of all records with matching options.
|
226
|
+
Retrieves list of all records with matching options.
|
268
227
|
|
269
228
|
**Note:** Some records (Invoice, CreditNote) only return summary information for the contact and no line items
|
270
|
-
when returning them this list operation. This library takes care of automatically retrieving the
|
229
|
+
when returning them this list operation. This library takes care of automatically retrieving the
|
271
230
|
contact and line items from Xero on first access however, this first access has a large performance penalty
|
272
231
|
and will count as an extra query towards your 5,000/day and 60/minute request per organisation limit.
|
273
232
|
|
@@ -281,6 +240,18 @@ Valid options are:
|
|
281
240
|
|
282
241
|
> Field to order by. Should be formatted as Xero-based field (e.g. 'Name', 'ContactID', etc)
|
283
242
|
|
243
|
+
> **:status**
|
244
|
+
|
245
|
+
> Status field for PurchaseOrder. Should be a valid Xero purchase order status.
|
246
|
+
|
247
|
+
> **:date_from**
|
248
|
+
|
249
|
+
> DateFrom field for PurchaseOrder. Should be in YYYY-MM-DD format.
|
250
|
+
|
251
|
+
> **:date_to**
|
252
|
+
|
253
|
+
> DateTo field for PurchaseOrder. Should be in YYYY-MM-DD format.
|
254
|
+
|
284
255
|
> **:where**
|
285
256
|
|
286
257
|
> __See *Where Filters* section below.__
|
@@ -305,11 +276,11 @@ You can specify find filters by providing the :where option with a hash. For exa
|
|
305
276
|
```ruby
|
306
277
|
invoices = Xero.Invoice.all(:where => {:type => 'ACCREC', :amount_due_is_not => 0})
|
307
278
|
```
|
308
|
-
|
279
|
+
|
309
280
|
will automatically create the Xero string:
|
310
281
|
|
311
282
|
Type=="ACCREC"&&AmountDue<>0
|
312
|
-
|
283
|
+
|
313
284
|
The default method for filtering is the equality '==' operator however, these can be overridden
|
314
285
|
by modifying the postfix of the attribute name (as you can see for the :amount\_due field above).
|
315
286
|
|
@@ -318,9 +289,9 @@ by modifying the postfix of the attribute name (as you can see for the :amount\_
|
|
318
289
|
\{attribute_name}_is_greater_than_or_equal_to will use '>='
|
319
290
|
\{attribute_name}_is_less_than will use '<'
|
320
291
|
\{attribute_name}_is_less_than_or_equal_to will use '<='
|
321
|
-
|
292
|
+
|
322
293
|
The default is '=='
|
323
|
-
|
294
|
+
|
324
295
|
**Note:** Currently, the hash-conversion library only allows for AND-based criteria and doesn't
|
325
296
|
take into account associations. For these, please use the custom filter method below.
|
326
297
|
|
@@ -332,15 +303,15 @@ in the resulting response, including all nested XML elements.
|
|
332
303
|
**Example 1: Retrieve all invoices for a specific contact ID:**
|
333
304
|
|
334
305
|
invoices = xero.Invoice.all(:where => 'Contact.ContactID.ToString()=="cd09aa49-134d-40fb-a52b-b63c6a91d712"')
|
335
|
-
|
306
|
+
|
336
307
|
**Example 2: Retrieve all unpaid ACCREC Invoices against a particular Contact Name:**
|
337
|
-
|
308
|
+
|
338
309
|
invoices = xero.Invoice.all(:where => 'Contact.Name=="Basket Case" && Type=="ACCREC" && AmountDue<>0')
|
339
|
-
|
310
|
+
|
340
311
|
**Example 3: Retrieve all Invoices PAID between certain dates**
|
341
|
-
|
312
|
+
|
342
313
|
invoices = xero.Invoice.all(:where => 'FullyPaidOnDate>=DateTime.Parse("2010-01-01T00:00:00")&&FullyPaidOnDate<=DateTime.Parse("2010-01-08T00:00:00")')
|
343
|
-
|
314
|
+
|
344
315
|
**Example 4: Retrieve all Invoices using Paging (batches of 100)**
|
345
316
|
|
346
317
|
invoices = xero.Invoice.find_in_batches({page_number: 1}) do |invoice_batch|
|
@@ -348,15 +319,15 @@ in the resulting response, including all nested XML elements.
|
|
348
319
|
...
|
349
320
|
end
|
350
321
|
end
|
351
|
-
|
322
|
+
|
352
323
|
**Example 5: Retrieve all Bank Accounts:**
|
353
|
-
|
324
|
+
|
354
325
|
accounts = xero.Account.all(:where => 'Type=="BANK"')
|
355
|
-
|
326
|
+
|
356
327
|
**Example 6: Retrieve all DELETED or VOIDED Invoices:**
|
357
|
-
|
328
|
+
|
358
329
|
invoices = xero.Invoice.all(:where => 'Status=="VOIDED" OR Status=="DELETED"')
|
359
|
-
|
330
|
+
|
360
331
|
**Example 7: Retrieve all contacts with specific text in the contact name:**
|
361
332
|
|
362
333
|
contacts = xero.Contact.all(:where => 'Name.Contains("Peter")')
|
@@ -376,7 +347,7 @@ invoice.line_items.each do | line_item |
|
|
376
347
|
puts "Line Description: #{line_item.description}"
|
377
348
|
end
|
378
349
|
```
|
379
|
-
|
350
|
+
|
380
351
|
**belongs\_to example:**
|
381
352
|
|
382
353
|
```ruby
|
@@ -430,21 +401,21 @@ contact.add_phone(:type => 'DEFAULT', :area_code => '07', :number => '3033 1234'
|
|
430
401
|
contact.add_phone(:type => 'MOBILE', :number => '0412 123 456')
|
431
402
|
contact.save
|
432
403
|
```
|
433
|
-
|
404
|
+
|
434
405
|
To add to a `has_many` association use the `add_{association}` method. For example:
|
435
406
|
|
436
407
|
```ruby
|
437
408
|
contact.add_address(:type => 'STREET', :line1 => '12 Testing Lane', :city => 'Brisbane')
|
438
409
|
```
|
439
|
-
|
410
|
+
|
440
411
|
To add to a `belongs_to` association use the `build_{association}` method. For example:
|
441
|
-
|
412
|
+
|
442
413
|
```ruby
|
443
414
|
invoice.build_contact(:name => 'ABC Company')
|
444
415
|
```
|
445
416
|
|
446
417
|
### Updating
|
447
|
-
|
418
|
+
|
448
419
|
If the primary GUID for the record is present, the library will attempt to update the record instead of
|
449
420
|
creating it. It is important that this record is downloaded from the Xero API first before attempting
|
450
421
|
an update. For example:
|
@@ -454,10 +425,17 @@ contact = xero.Contact.find("cd09aa49-134d-40fb-a52b-b63c6a91d712")
|
|
454
425
|
contact.name = "Another Name Change"
|
455
426
|
contact.save
|
456
427
|
```
|
457
|
-
|
458
|
-
Have a look at the models in `lib/xeroizer/models/` to see the valid attributes, associations and
|
428
|
+
|
429
|
+
Have a look at the models in `lib/xeroizer/models/` to see the valid attributes, associations and
|
459
430
|
minimum validation requirements for each of the record types.
|
460
431
|
|
432
|
+
Some Xero endpoints, such as Payment, will only accept specific attributes for updates. Because the library does not have this knowledge encoded (and doesn't do dirty tracking of attributes), it's necessary to construct new objects instead of using the ones retrieved from Xero:
|
433
|
+
|
434
|
+
```ruby
|
435
|
+
delete_payment = gateway.Payment.build(id: payment.id, status: 'DELETED')
|
436
|
+
delete_payment.save
|
437
|
+
```
|
438
|
+
|
461
439
|
### Bulk Creates & Updates
|
462
440
|
|
463
441
|
Xero has a hard daily limit on the number of API requests you can make (currently 5,000 requests
|
@@ -501,7 +479,7 @@ saved = contact.save
|
|
501
479
|
|
502
480
|
# contact.errors will contain [[:name, "can't be blank"]]
|
503
481
|
```
|
504
|
-
|
482
|
+
|
505
483
|
\#errors\_for(:attribute\_name) is a helper method to return just the errors associated with
|
506
484
|
that attribute. For example:
|
507
485
|
|
@@ -530,7 +508,7 @@ invoice.save
|
|
530
508
|
# An invoice created without a status will default to 'DRAFT'
|
531
509
|
invoice.approved?
|
532
510
|
|
533
|
-
# Payments can only be created against '
|
511
|
+
# Payments can only be created against 'AUTHORISED' invoices
|
534
512
|
invoice.approve!
|
535
513
|
|
536
514
|
# Find the first bank account
|
@@ -563,6 +541,8 @@ Reports are accessed like the following example:
|
|
563
541
|
```ruby
|
564
542
|
trial_balance = xero.TrialBalance.get(:date => DateTime.new(2011,3,21))
|
565
543
|
|
544
|
+
profit_and_loss = xero.ProfitAndLoss.get(fromDate: Date.new(2019,4,1), toDate: Date.new(2019,5,1))
|
545
|
+
|
566
546
|
# Array containing report headings.
|
567
547
|
trial_balance.header.cells.map { | cell | cell.value }
|
568
548
|
|
@@ -582,16 +562,16 @@ trial_balance.rows.each do | row |
|
|
582
562
|
case row
|
583
563
|
when Xeroizer::Report::HeaderRow
|
584
564
|
# do something with header
|
585
|
-
|
565
|
+
|
586
566
|
when Xeroizer::Report::SectionRow
|
587
567
|
# do something with section, will need to step into the rows for this section
|
588
|
-
|
568
|
+
|
589
569
|
when Xeroizer::Report::Row
|
590
570
|
# do something for standard report rows
|
591
|
-
|
571
|
+
|
592
572
|
when Xeroizer::Report::SummaryRow
|
593
573
|
# do something for summary rows
|
594
|
-
|
574
|
+
|
595
575
|
end
|
596
576
|
end
|
597
577
|
```
|
@@ -613,8 +593,8 @@ You can set this option when initializing an application:
|
|
613
593
|
|
614
594
|
```ruby
|
615
595
|
# Sleep for 2 seconds every time the rate limit is exceeded.
|
616
|
-
client = Xeroizer::
|
617
|
-
|
596
|
+
client = Xeroizer::OAuth2Application.new(YOUR_OAUTH2_CLIENT_ID,
|
597
|
+
YOUR_OAUTH2_CLIENT_SECRET,
|
618
598
|
:rate_limit_sleep => 2)
|
619
599
|
```
|
620
600
|
|
@@ -630,9 +610,9 @@ If required, the library can handle these exceptions internally by sleeping 1 se
|
|
630
610
|
You can set this option when initializing an application:
|
631
611
|
|
632
612
|
```ruby
|
633
|
-
# Sleep for
|
634
|
-
client = Xeroizer::
|
635
|
-
|
613
|
+
# Sleep for 1 second and retry up to 3 times when Xero claims the nonce was used.
|
614
|
+
client = Xeroizer::OAuth2Application.new(YOUR_OAUTH2_CLIENT_ID,
|
615
|
+
YOUR_OAUTH2_CLIENT_SECRET,
|
636
616
|
:nonce_used_max_attempts => 3)
|
637
617
|
```
|
638
618
|
|
@@ -644,8 +624,8 @@ You can add an optional parameter to the Xeroizer Application initialization, to
|
|
644
624
|
|
645
625
|
```ruby
|
646
626
|
XeroLogger = Logger.new('log/xero.log', 'weekly')
|
647
|
-
client = Xeroizer::
|
648
|
-
|
627
|
+
client = Xeroizer::OAuth2Application.new(YOUR_OAUTH2_CLIENT_ID,
|
628
|
+
YOUR_OAUTH2_CLIENT_SECRET,
|
649
629
|
:logger => XeroLogger)
|
650
630
|
```
|
651
631
|
|
@@ -657,10 +637,10 @@ time Xeroizer makes an HTTP request, which is potentially useful for both
|
|
657
637
|
throttling and logging:
|
658
638
|
|
659
639
|
```ruby
|
660
|
-
Xeroizer::
|
640
|
+
Xeroizer::OAuth2Application.new(
|
661
641
|
credentials[:key], credentials[:secret],
|
662
642
|
before_request: ->(request) { puts "Hitting this URL: #{request.url}" },
|
663
|
-
after_request: ->(request, response) { puts "Got this response: #{response.code}" }
|
643
|
+
after_request: ->(request, response) { puts "Got this response: #{response.code}" },
|
664
644
|
around_request: -> (request, &block) { puts "About to send request"; block.call; puts "After request"}
|
665
645
|
)
|
666
646
|
```
|
@@ -676,8 +656,8 @@ By default, the API accepts unit prices (UnitAmount) to two decimals places. If
|
|
676
656
|
|
677
657
|
|
678
658
|
```ruby
|
679
|
-
client = Xeroizer::
|
680
|
-
|
659
|
+
client = Xeroizer::OAuth2Application.new(YOUR_OAUTH2_CLIENT_ID,
|
660
|
+
YOUR_OAUTH2_CLIENT_SECRET,
|
681
661
|
:unitdp => 4)
|
682
662
|
```
|
683
663
|
|
@@ -686,24 +666,20 @@ This option adds the unitdp=4 query string parameter to all requests for models
|
|
686
666
|
Tests
|
687
667
|
-----
|
688
668
|
|
689
|
-
|
690
|
-
|
691
|
-
Once you have created your Private App, set these environment variables:
|
692
|
-
```
|
693
|
-
EXPORT CONSUMER_KEY="your private app's consumer key"
|
694
|
-
EXPORT CONSUMER_SECRET="your private app's consumer secret"
|
695
|
-
EXPORT PRIVATE_KEY_PATH="the path to your private app's private key"
|
696
|
-
```
|
669
|
+
OAuth2 Tests
|
697
670
|
|
698
|
-
|
671
|
+
The tests within the repository can be run by setting up a [OAuth2 App](https://developer.xero.com/documentation/guides/oauth2/auth-flow/). You can create a Private App in the [developer portal](https://developer.xero.com/myapps/), it's suggested that you create it against the [Demo Company (AU)](https://developer.xero.com/documentation/getting-started/development-accounts). Demo Company expires after 28 days, so you will need to reset it and re-connect to it if your Demo Company has expired. Make sure you create the Demo Company in Australia region.
|
699
672
|
|
700
|
-
Then run the tests
|
701
673
|
```
|
674
|
+
export XERO_CLIENT_ID="asd"
|
675
|
+
export XERO_CLIENT_SECRET="asdfg"
|
676
|
+
export XERO_ACCESS_TOKEN="sadfsdf"
|
677
|
+
export XERO_TENANT_ID="asdfasdfasdfasd"
|
678
|
+
|
702
679
|
rake test
|
703
680
|
```
|
704
681
|
|
705
|
-
|
706
682
|
### Contributors
|
707
|
-
Xeroizer was inspired by the https://github.com/tlconnor/xero_gateway gem created by Tim Connor
|
708
|
-
and Nik Wakelin and portions of the networking and authentication code are based completely off
|
683
|
+
Xeroizer was inspired by the https://github.com/tlconnor/xero_gateway gem created by Tim Connor
|
684
|
+
and Nik Wakelin and portions of the networking and authentication code are based completely off
|
709
685
|
this project. Copyright for these components remains held in the name of Tim Connor.
|