xeroizer 2.17.1 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (136) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +246 -213
  3. data/lib/xeroizer/connection.rb +49 -0
  4. data/lib/xeroizer/exceptions.rb +4 -0
  5. data/lib/xeroizer/generic_application.rb +13 -5
  6. data/lib/xeroizer/http.rb +7 -80
  7. data/lib/xeroizer/http_response.rb +154 -0
  8. data/lib/xeroizer/models/bank_account.rb +1 -0
  9. data/lib/xeroizer/models/bank_transaction.rb +1 -0
  10. data/lib/xeroizer/models/batch_payment.rb +27 -0
  11. data/lib/xeroizer/models/branding_theme.rb +49 -9
  12. data/lib/xeroizer/models/contact.rb +12 -6
  13. data/lib/xeroizer/models/contact_group.rb +45 -0
  14. data/lib/xeroizer/models/credit_note.rb +24 -22
  15. data/lib/xeroizer/models/currency.rb +14 -2
  16. data/lib/xeroizer/models/from_bank_account.rb +1 -0
  17. data/lib/xeroizer/models/history_record.rb +72 -0
  18. data/lib/xeroizer/models/invoice.rb +17 -3
  19. data/lib/xeroizer/models/item.rb +2 -1
  20. data/lib/xeroizer/models/item_purchase_details.rb +1 -1
  21. data/lib/xeroizer/models/line_item.rb +17 -5
  22. data/lib/xeroizer/models/manual_journal.rb +2 -1
  23. data/lib/xeroizer/models/online_invoice.rb +37 -0
  24. data/lib/xeroizer/models/option.rb +1 -1
  25. data/lib/xeroizer/models/organisation.rb +2 -0
  26. data/lib/xeroizer/models/payment_service.rb +22 -0
  27. data/lib/xeroizer/models/payroll/address.rb +53 -0
  28. data/lib/xeroizer/models/payroll/bank_account.rb +18 -6
  29. data/lib/xeroizer/models/payroll/benefit_line.rb +26 -0
  30. data/lib/xeroizer/models/payroll/benefit_type.rb +45 -0
  31. data/lib/xeroizer/models/payroll/deduction_line.rb +32 -0
  32. data/lib/xeroizer/models/payroll/deduction_type.rb +49 -0
  33. data/lib/xeroizer/models/payroll/earnings_line.rb +39 -0
  34. data/lib/xeroizer/models/payroll/earnings_type.rb +53 -0
  35. data/lib/xeroizer/models/payroll/employee.rb +30 -8
  36. data/lib/xeroizer/models/payroll/leave_application.rb +27 -0
  37. data/lib/xeroizer/models/payroll/leave_line.rb +30 -0
  38. data/lib/xeroizer/models/payroll/leave_period.rb +15 -0
  39. data/lib/xeroizer/models/payroll/pay_items.rb +22 -0
  40. data/lib/xeroizer/models/payroll/pay_run.rb +33 -0
  41. data/lib/xeroizer/models/payroll/pay_schedule.rb +40 -0
  42. data/lib/xeroizer/models/payroll/pay_template.rb +24 -0
  43. data/lib/xeroizer/models/payroll/payment_method.rb +24 -0
  44. data/lib/xeroizer/models/payroll/paystub.rb +44 -0
  45. data/lib/xeroizer/models/payroll/reimbursement_line.rb +21 -0
  46. data/lib/xeroizer/models/payroll/reimbursement_type.rb +22 -0
  47. data/lib/xeroizer/models/payroll/salary_and_wage.rb +29 -0
  48. data/lib/xeroizer/models/payroll/super_line.rb +40 -0
  49. data/lib/xeroizer/models/payroll/tax_declaration.rb +50 -0
  50. data/lib/xeroizer/models/payroll/time_off_line.rb +20 -0
  51. data/lib/xeroizer/models/payroll/time_off_type.rb +32 -0
  52. data/lib/xeroizer/models/payroll/work_location.rb +25 -0
  53. data/lib/xeroizer/models/prepayment.rb +1 -0
  54. data/lib/xeroizer/models/purchase_order.rb +6 -6
  55. data/lib/xeroizer/models/quote.rb +76 -0
  56. data/lib/xeroizer/models/schedule.rb +1 -0
  57. data/lib/xeroizer/models/tax_component.rb +1 -0
  58. data/lib/xeroizer/models/to_bank_account.rb +1 -0
  59. data/lib/xeroizer/oauth.rb +12 -1
  60. data/lib/xeroizer/oauth2.rb +82 -0
  61. data/lib/xeroizer/oauth2_application.rb +49 -0
  62. data/lib/xeroizer/payroll_application.rb +8 -3
  63. data/lib/xeroizer/record/base.rb +11 -2
  64. data/lib/xeroizer/record/base_model.rb +1 -1
  65. data/lib/xeroizer/record/base_model_http_proxy.rb +37 -17
  66. data/lib/xeroizer/record/model_definition_helper.rb +1 -1
  67. data/lib/xeroizer/record/payroll_base.rb +4 -0
  68. data/lib/xeroizer/record/record_association_helper.rb +4 -4
  69. data/lib/xeroizer/record/validators/associated_validator.rb +1 -0
  70. data/lib/xeroizer/record/xml_helper.rb +18 -18
  71. data/lib/xeroizer/report/aged_receivables_by_contact.rb +1 -1
  72. data/lib/xeroizer/report/cell_xml_helper.rb +13 -13
  73. data/lib/xeroizer/response.rb +22 -17
  74. data/lib/xeroizer/version.rb +1 -1
  75. data/lib/xeroizer.rb +34 -4
  76. data/test/acceptance/about_creating_bank_transactions_test.rb +89 -81
  77. data/test/acceptance/about_creating_prepayment_test.rb +25 -30
  78. data/test/acceptance/about_fetching_bank_transactions_test.rb +12 -12
  79. data/test/acceptance/about_online_invoice_test.rb +25 -0
  80. data/test/acceptance/acceptance_test.rb +28 -26
  81. data/test/acceptance/bank_transfer_test.rb +12 -17
  82. data/test/acceptance/bulk_operations_test.rb +18 -16
  83. data/test/acceptance/connections_test.rb +11 -0
  84. data/test/stub_responses/bad_request.json +6 -0
  85. data/test/stub_responses/connections.json +16 -0
  86. data/test/stub_responses/expired_oauth2_token.json +6 -0
  87. data/test/stub_responses/generic_response_error.json +6 -0
  88. data/test/stub_responses/invalid_oauth2_request_token.json +6 -0
  89. data/test/stub_responses/invalid_tenant_header.json +6 -0
  90. data/test/stub_responses/object_not_found.json +6 -0
  91. data/test/stub_responses/organisations.xml +10 -0
  92. data/test/stub_responses/payment_service.xml +15 -0
  93. data/test/test_helper.rb +17 -12
  94. data/test/unit/generic_application_test.rb +21 -10
  95. data/test/unit/http_test.rb +282 -10
  96. data/test/unit/models/address_test.rb +2 -2
  97. data/test/unit/models/bank_transaction_model_parsing_test.rb +2 -2
  98. data/test/unit/models/bank_transaction_test.rb +1 -1
  99. data/test/unit/models/bank_transaction_validation_test.rb +1 -1
  100. data/test/unit/models/contact_test.rb +20 -11
  101. data/test/unit/models/credit_note_test.rb +8 -8
  102. data/test/unit/models/employee_test.rb +4 -4
  103. data/test/unit/models/invoice_test.rb +12 -12
  104. data/test/unit/models/journal_line_test.rb +6 -6
  105. data/test/unit/models/journal_test.rb +4 -4
  106. data/test/unit/models/line_item_sum_test.rb +1 -1
  107. data/test/unit/models/line_item_test.rb +29 -37
  108. data/test/unit/models/manual_journal_test.rb +3 -3
  109. data/test/unit/models/organisation_test.rb +16 -2
  110. data/test/unit/models/payment_service_test.rb +29 -0
  111. data/test/unit/models/phone_test.rb +7 -7
  112. data/test/unit/models/prepayment_test.rb +4 -4
  113. data/test/unit/models/repeating_invoice_test.rb +3 -3
  114. data/test/unit/models/tax_rate_test.rb +2 -2
  115. data/test/unit/oauth2_test.rb +171 -0
  116. data/test/unit/oauth_config_test.rb +1 -1
  117. data/test/unit/record/base_model_test.rb +13 -13
  118. data/test/unit/record/base_test.rb +73 -4
  119. data/test/unit/record/block_validator_test.rb +1 -1
  120. data/test/unit/record/connection_test.rb +60 -0
  121. data/test/unit/record/model_definition_test.rb +36 -36
  122. data/test/unit/record/parse_params_test.rb +59 -0
  123. data/test/unit/record/parse_where_hash_test.rb +13 -13
  124. data/test/unit/record/record_association_test.rb +14 -14
  125. data/test/unit/record/validators_test.rb +43 -43
  126. data/test/unit/record_definition_test.rb +7 -7
  127. data/test/unit/report_definition_test.rb +7 -7
  128. data/test/unit/report_test.rb +20 -20
  129. data/test/unit_test_helper.rb +16 -0
  130. metadata +117 -27
  131. data/lib/xeroizer/models/payroll/home_address.rb +0 -24
  132. data/lib/xeroizer/partner_application.rb +0 -51
  133. data/lib/xeroizer/private_application.rb +0 -25
  134. data/lib/xeroizer/public_application.rb +0 -21
  135. data/test/unit/oauth_test.rb +0 -118
  136. data/test/unit/private_application_test.rb +0 -20
data/README.md CHANGED
@@ -1,13 +1,20 @@
1
- Xeroizer API Library ![Project status](http://stillmaintained.com/waynerobinson/xeroizer.png) [![Build Status](https://travis-ci.org/waynerobinson/xeroizer.svg)](https://travis-ci.org/waynerobinson/xeroizer)
1
+ Xeroizer API Library
2
2
  ====================
3
3
 
4
- **Homepage**: [http://waynerobinson.github.com/xeroizer](http://waynerobinson.github.com/xeroizer)
5
- **Git**: [git://github.com/waynerobinson/xeroizer.git](git://github.com/waynerobinson/xeroizer.git)
6
- **Github**: [https://github.com/waynerobinson/xeroizer](https://github.com/waynerobinson/xeroizer)
7
- **Author**: Wayne Robinson [http://www.wayne-robinson.com](http://www.wayne-robinson.com)
8
- **Contributors**: See Contributors section below
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
- **License**: MIT License
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::PublicApplication.new(YOUR_OAUTH_CONSUMER_KEY, YOUR_OAUTH_CONSUMER_SECRET)
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
- request_token = @xero_client.request_token(:oauth_callback => 'http://yourapp.com/xero_session/create')
101
- session[:request_token] = request_token.token
102
- session[:request_secret] = request_token.secret
103
-
104
- redirect_to request_token.authorize_url
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.authorize_from_request(
109
- session[:request_token],
110
- session[:request_secret],
111
- :oauth_verifier => params[:oauth_verifier] )
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
- :access_token => @xero_client.access_token.token,
115
- :access_key => @xero_client.access_token.secret }
116
-
117
- session.data.delete(:request_token)
118
- session.data.delete(:request_secret)
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::PublicApplication.new(YOUR_OAUTH_CONSUMER_KEY, YOUR_OAUTH_CONSUMER_SECRET)
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.authorize_from_access(
133
- session[:xero_auth][:access_token],
134
- session[:xero_auth][:access_key] )
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
- You can store the access token/secret pair so you can access the API again without user intervention. Currently these
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
- If you want API access for longer consider creating a PartnerApplication which will allow you to renew tokens.
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
- access_key = client.access_token.token
150
- access_secret = client.access_token.secret
151
- ```
152
-
153
- ### Private Applications
154
-
155
- Private applications use a 2-legged authorisation process. When you register your application, you will select
156
- the organisation that is authorised to your application. This cannot be changed afterwards, although you can
157
- register another private application if you have multiple organisations.
158
-
159
- Note: You can only register organisations you are authorised to yourself.
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
- You need to upload this `public_privatekey.pfx` file to your private application in [http://api.xero.com](http://api.xero.com).
127
+ # Rails as an example
128
+ redirect_to url
129
+ ```
169
130
 
170
- Example usage:
131
+ 2. In the callback route, use the provided code to retrieve an access token.
171
132
 
172
133
  ```ruby
173
- client = Xeroizer::PrivateApplication.new(YOUR_OAUTH_CONSUMER_KEY, YOUR_OAUTH_CONSUMER_SECRET, "/path/to/privatekey.pem")
174
- contacts = client.Contact.all
175
- ```
176
-
177
- ### Partner Applications
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
- Partner applications use a combination of 3-legged authorisation and private key message signing.
147
+ # Save the access_token, refresh_token...
148
+ ```
180
149
 
181
- You will need to contact Xero (network@xero.com) to get permission to create a partner application.
150
+ 3. Retrieve the tenant ids.
151
+ ```ruby
152
+ connections = client.current_connections
153
+ # returns Xeroizer::Connection instances
182
154
 
183
- After you have followed the instructions provided by Xero for partner applications and uploaded your certificate you can
184
- access the partner application in a similar way to public applications.
155
+ # Save the tenant ids
185
156
 
186
- Authentication occcurs in 3 steps:
157
+ ```
187
158
 
159
+ 4. Use access token and tenant ids to retrieve data.
188
160
  ```ruby
189
- client = Xeroizer::PartnerApplication.new(
190
- YOUR_OAUTH_CONSUMER_KEY,
191
- YOUR_OAUTH_CONSUMER_SECRET,
192
- "/path/to/privatekey.pem"
193
- )
194
-
195
- # 1. Get a RequestToken from Xero. :oauth_callback is the URL the user will be redirected to
196
- # after they have authenticated your application.
197
- #
198
- # Note: The callback URL's domain must match that listed for your application in http://api.xero.com
199
- # otherwise the user will not be redirected and only be shown the authentication code.
200
- request_token = client.request_token(:oauth_callback => 'http://yourapp.com/oauth/callback')
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
- # 3. Exchange RequestToken for AccessToken.
208
- # This access token will be used for all subsequent requests but it is stored within the client
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
- This AccessToken will last for 30 minutes however, when using the partner application API you can
216
- renew this token. To be able to renew this token, you need to save the following data from this organisation's
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
- session_handle = client.session_handle
221
- access_key = client.access_token.token
222
- access_secret = client.access_token.secret
223
- ```
224
-
225
- Two other interesting attributes of the PartnerApplication client are:
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 365 days 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
- #### AccessToken Renewal
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
- Renewal of an access token requires knowledge of the previous access token generated for this organisation. To renew:
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
- # If you still have a client instance.
236
- client.renew_access_token
198
+ client = Xeroizer::OAuth2Application.new(
199
+ YOUR_OAUTH2_CLIENT_ID,
200
+ YOUR_OAUTH2_CLIENT_SECRET
201
+ )
237
202
 
238
- # If you are renewing from stored token/session details.
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 PublicApplication, PrivateApplication
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::PublicApplication.new(YOUR_OAUTH_CONSUMER_KEY, YOUR_OAUTH_CONSUMER_SECRET)
257
- xero.authorize_from_access(session[:xero_auth][:access_token], session[:xero_auth][:access_key])
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,12 +223,12 @@ 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
- and will count as an extra query towards your 1,000/day and 60/minute request per organisation limit.
231
+ and will count as an extra query towards your 5,000/day and 60/minute request per organisation limit.
273
232
 
274
233
  Valid options are:
275
234
 
@@ -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,13 +425,20 @@ 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
- Xero has a hard daily limit on the number of API requests you can make (currently 1,000 requests
441
+ Xero has a hard daily limit on the number of API requests you can make (currently 5,000 requests
464
442
  per account per day). To save on requests, you can batch creates and updates into a single PUT or
465
443
  POST call, like so:
466
444
 
@@ -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
 
@@ -512,6 +490,43 @@ contact.errors_for(:name) # will contain ["can't be blank"]
512
490
  If something goes really wrong and the particular validation isn't handled by the internal
513
491
  validators then the library may raise a `Xeroizer::ApiException`.
514
492
 
493
+ Example Use Cases
494
+ -------
495
+
496
+ Creating & Paying an invoice:
497
+
498
+ ```ruby
499
+ contact = xero.Contact.first
500
+
501
+ # Build the Invoice, add a LineItem and save it
502
+ invoice = xero.Invoice.build(:type => "ACCREC", :contact => contact, :date => DateTime.new(2017,10,19), :due_date => DateTime.new(2017,11,19))
503
+
504
+ invoice.add_line_item(:description => 'test', :unit_amount => '200.00', :quantity => '1', :account_code => '200')
505
+
506
+ invoice.save
507
+
508
+ # An invoice created without a status will default to 'DRAFT'
509
+ invoice.approved?
510
+
511
+ # Payments can only be created against 'AUTHORISED' invoices
512
+ invoice.approve!
513
+
514
+ # Find the first bank account
515
+ bank_account = xero.Account.first(:where => {:type => 'BANK'})
516
+
517
+ # Create & save the payment
518
+ payment = xero.Payment.build(:invoice => invoice, :account => bank_account, :amount => '220.00')
519
+ payment.save
520
+
521
+ # Reload the invoice from the Xero API
522
+ invoice = xero.Invoice.find(invoice.id)
523
+
524
+ # Invoice status is now "PAID" & Payment details have been returned as well
525
+ invoice.status
526
+ invoice.payments.first
527
+ invoice.payments.first.date
528
+ ```
529
+
515
530
  Reports
516
531
  -------
517
532
 
@@ -526,6 +541,8 @@ Reports are accessed like the following example:
526
541
  ```ruby
527
542
  trial_balance = xero.TrialBalance.get(:date => DateTime.new(2011,3,21))
528
543
 
544
+ profit_and_loss = xero.ProfitAndLoss.get(fromDate: Date.new(2019,4,1), toDate: Date.new(2019,5,1))
545
+
529
546
  # Array containing report headings.
530
547
  trial_balance.header.cells.map { | cell | cell.value }
531
548
 
@@ -545,16 +562,16 @@ trial_balance.rows.each do | row |
545
562
  case row
546
563
  when Xeroizer::Report::HeaderRow
547
564
  # do something with header
548
-
565
+
549
566
  when Xeroizer::Report::SectionRow
550
567
  # do something with section, will need to step into the rows for this section
551
-
568
+
552
569
  when Xeroizer::Report::Row
553
570
  # do something for standard report rows
554
-
571
+
555
572
  when Xeroizer::Report::SummaryRow
556
573
  # do something for summary rows
557
-
574
+
558
575
  end
559
576
  end
560
577
  ```
@@ -576,8 +593,8 @@ You can set this option when initializing an application:
576
593
 
577
594
  ```ruby
578
595
  # Sleep for 2 seconds every time the rate limit is exceeded.
579
- client = Xeroizer::PublicApplication.new(YOUR_OAUTH_CONSUMER_KEY,
580
- YOUR_OAUTH_CONSUMER_SECRET,
596
+ client = Xeroizer::OAuth2Application.new(YOUR_OAUTH2_CLIENT_ID,
597
+ YOUR_OAUTH2_CLIENT_SECRET,
581
598
  :rate_limit_sleep => 2)
582
599
  ```
583
600
 
@@ -593,9 +610,9 @@ If required, the library can handle these exceptions internally by sleeping 1 se
593
610
  You can set this option when initializing an application:
594
611
 
595
612
  ```ruby
596
- # Sleep for 2 seconds every time the rate limit is exceeded.
597
- client = Xeroizer::PublicApplication.new(YOUR_OAUTH_CONSUMER_KEY,
598
- YOUR_OAUTH_CONSUMER_SECRET,
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,
599
616
  :nonce_used_max_attempts => 3)
600
617
  ```
601
618
 
@@ -607,8 +624,8 @@ You can add an optional parameter to the Xeroizer Application initialization, to
607
624
 
608
625
  ```ruby
609
626
  XeroLogger = Logger.new('log/xero.log', 'weekly')
610
- client = Xeroizer::PublicApplication.new(YOUR_OAUTH_CONSUMER_KEY,
611
- YOUR_OAUTH_CONSUMER_SECRET,
627
+ client = Xeroizer::OAuth2Application.new(YOUR_OAUTH2_CLIENT_ID,
628
+ YOUR_OAUTH2_CLIENT_SECRET,
612
629
  :logger => XeroLogger)
613
630
  ```
614
631
 
@@ -620,10 +637,10 @@ time Xeroizer makes an HTTP request, which is potentially useful for both
620
637
  throttling and logging:
621
638
 
622
639
  ```ruby
623
- Xeroizer::PublicApplication.new(
640
+ Xeroizer::OAuth2Application.new(
624
641
  credentials[:key], credentials[:secret],
625
642
  before_request: ->(request) { puts "Hitting this URL: #{request.url}" },
626
- after_request: ->(request, response) { puts "Got this response: #{response.code}" }
643
+ after_request: ->(request, response) { puts "Got this response: #{response.code}" },
627
644
  around_request: -> (request, &block) { puts "About to send request"; block.call; puts "After request"}
628
645
  )
629
646
  ```
@@ -639,14 +656,30 @@ By default, the API accepts unit prices (UnitAmount) to two decimals places. If
639
656
 
640
657
 
641
658
  ```ruby
642
- client = Xeroizer::PublicApplication.new(YOUR_OAUTH_CONSUMER_KEY,
643
- YOUR_OAUTH_CONSUMER_SECRET,
659
+ client = Xeroizer::OAuth2Application.new(YOUR_OAUTH2_CLIENT_ID,
660
+ YOUR_OAUTH2_CLIENT_SECRET,
644
661
  :unitdp => 4)
645
662
  ```
646
663
 
647
664
  This option adds the unitdp=4 query string parameter to all requests for models with line items - invoices, credit notes, bank transactions and receipts.
648
665
 
666
+ Tests
667
+ -----
668
+
669
+ OAuth2 Tests
670
+
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.
672
+
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
+
679
+ rake test
680
+ ```
681
+
649
682
  ### Contributors
650
- Xeroizer was inspired by the https://github.com/tlconnor/xero_gateway gem created by Tim Connor
651
- 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
652
685
  this project. Copyright for these components remains held in the name of Tim Connor.