xeroizer 2.16.5 → 2.17.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +21 -27
  3. data/lib/class_level_inheritable_attributes.rb +3 -0
  4. data/lib/xeroizer.rb +13 -1
  5. data/lib/xeroizer/exceptions.rb +1 -1
  6. data/lib/xeroizer/generic_application.rb +13 -1
  7. data/lib/xeroizer/http.rb +128 -120
  8. data/lib/xeroizer/models/account.rb +1 -0
  9. data/lib/xeroizer/models/address.rb +18 -9
  10. data/lib/xeroizer/models/attachment.rb +1 -1
  11. data/lib/xeroizer/models/balances.rb +1 -0
  12. data/lib/xeroizer/models/bank_transaction.rb +2 -2
  13. data/lib/xeroizer/models/bank_transfer.rb +28 -0
  14. data/lib/xeroizer/models/contact.rb +11 -4
  15. data/lib/xeroizer/models/credit_note.rb +9 -4
  16. data/lib/xeroizer/models/from_bank_account.rb +12 -0
  17. data/lib/xeroizer/models/invoice.rb +11 -3
  18. data/lib/xeroizer/models/invoice_reminder.rb +19 -0
  19. data/lib/xeroizer/models/line_item.rb +21 -9
  20. data/lib/xeroizer/models/manual_journal.rb +6 -0
  21. data/lib/xeroizer/models/organisation.rb +4 -0
  22. data/lib/xeroizer/models/overpayment.rb +40 -0
  23. data/lib/xeroizer/models/payroll/bank_account.rb +23 -0
  24. data/lib/xeroizer/models/payroll/employee.rb +45 -0
  25. data/lib/xeroizer/models/payroll/home_address.rb +24 -0
  26. data/lib/xeroizer/models/prepayment.rb +1 -0
  27. data/lib/xeroizer/models/tax_rate.rb +5 -4
  28. data/lib/xeroizer/models/to_bank_account.rb +12 -0
  29. data/lib/xeroizer/oauth.rb +8 -8
  30. data/lib/xeroizer/partner_application.rb +4 -8
  31. data/lib/xeroizer/payroll_application.rb +28 -0
  32. data/lib/xeroizer/private_application.rb +2 -4
  33. data/lib/xeroizer/record/base_model.rb +137 -122
  34. data/lib/xeroizer/record/base_model_http_proxy.rb +1 -1
  35. data/lib/xeroizer/record/payroll_base.rb +25 -0
  36. data/lib/xeroizer/record/payroll_base_model.rb +29 -0
  37. data/lib/xeroizer/record/record_association_helper.rb +13 -14
  38. data/lib/xeroizer/record/validation_helper.rb +1 -1
  39. data/lib/xeroizer/record/xml_helper.rb +10 -8
  40. data/lib/xeroizer/report/aged_receivables_by_contact.rb +0 -1
  41. data/lib/xeroizer/report/base.rb +0 -1
  42. data/lib/xeroizer/report/factory.rb +3 -3
  43. data/lib/xeroizer/report/row/header.rb +4 -4
  44. data/lib/xeroizer/report/row/xml_helper.rb +2 -2
  45. data/lib/xeroizer/version.rb +1 -1
  46. data/test/acceptance/about_creating_prepayment_test.rb +46 -0
  47. data/test/acceptance/about_fetching_bank_transactions_test.rb +21 -21
  48. data/test/acceptance/acceptance_test.rb +4 -4
  49. data/test/acceptance/bank_transaction_reference_data.rb +3 -1
  50. data/test/acceptance/bank_transfer_test.rb +26 -0
  51. data/test/test_helper.rb +6 -4
  52. data/test/unit/models/address_test.rb +92 -0
  53. data/test/unit/models/bank_transaction_validation_test.rb +1 -1
  54. data/test/unit/models/contact_test.rb +20 -4
  55. data/test/unit/models/line_item_test.rb +20 -6
  56. data/test/unit/models/tax_rate_test.rb +52 -1
  57. data/test/unit/record/base_model_test.rb +1 -1
  58. data/test/unit/record/base_test.rb +9 -6
  59. data/test/unit/record/model_definition_test.rb +2 -2
  60. data/test/unit/report_test.rb +19 -21
  61. metadata +20 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 661b00281b09048f7c58f3bc0e4c12d3401fc9c3
4
- data.tar.gz: 79db1f4fa930e1deb41f78fd0cee48ee6e30ab95
3
+ metadata.gz: 0b4149d832f1cb36887a460f7f8258d049fdd991
4
+ data.tar.gz: c8ea7a95afb25ee8f41f7a1f15f9f6ce4ae3914e
5
5
  SHA512:
6
- metadata.gz: 5dec38c448515e18f9aad030a0ae05bcd7504133474fcc8b52d06be00947db73556416414bb528483810d894a97785d862540b4d7a4cdd948632b99c5ef0abdf
7
- data.tar.gz: 091ef6309c0a17f85976b1135eaca233de6b3e7031b972bfbdc14c81b9016c67177f6fdf1ebdca9694047aba232a5f5c6d7e95cf2f1f1f49c13c3cab7b139156
6
+ metadata.gz: bbaef5a37d2b986f9e81f7b2cd229f501d711563f5634f2cc50125f541cca510386e5110dae6a483fd4782db4213641cc827ab6a6cab43b33bc43deaeb68c6ea
7
+ data.tar.gz: e37566c54ffddbc031712a08ff0c3179ea26f8def7f75c709d9430ed01a185f70f639976741599bef4307ac9846034c533c060e982129311f23f7f0aaf60b884
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- Xeroizer API Library ![Project status](http://stillmaintained.com/waynerobinson/xeroizer.png)
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)
2
2
  ====================
3
3
 
4
4
  **Homepage**: [http://waynerobinson.github.com/xeroizer](http://waynerobinson.github.com/xeroizer)
@@ -114,8 +114,8 @@ class XeroSessionController < ApplicationController
114
114
  :access_token => @xero_client.access_token.token,
115
115
  :access_key => @xero_client.access_token.secret }
116
116
 
117
- session[:request_token] = nil
118
- session[:request_secret] = nil
117
+ session.data.delete(:request_token)
118
+ session.data.delete(:request_secret)
119
119
  end
120
120
 
121
121
  def destroy
@@ -176,22 +176,9 @@ contacts = client.Contact.all
176
176
 
177
177
  ### Partner Applications
178
178
 
179
- Partner applications use a combination of 3-legged authorisation, private key message signing and client-side SSL
180
- certificate signing.
179
+ Partner applications use a combination of 3-legged authorisation and private key message signing.
181
180
 
182
- Partner applications are only in beta testing via the Xero API and you will need to contact Xero (network@xero.com) to
183
- get permission to create a partner application and for them to send you information on obtaining your client-side SSL
184
- certificate.
185
-
186
- Ruby's OpenSSL library requires the certificate and private key to be extracted from the `entrust-client.p12` file
187
- downloaded via Xero's instructions. To extract:
188
-
189
- openssl pkcs12 -in entrust-client.p12 -clcerts -nokeys -out entrust-cert.pem
190
- openssl pkcs12 -in entrust-client.p12 -nocerts -out entrust-private.pem
191
- openssl rsa -in entrust-private.pem -out entrust-private-nopass.pem
192
-
193
- # This last step removes the password that you added to the private key
194
- # when it was exported.
181
+ You will need to contact Xero (network@xero.com) to get permission to create a partner application.
195
182
 
196
183
  After you have followed the instructions provided by Xero for partner applications and uploaded your certificate you can
197
184
  access the partner application in a similar way to public applications.
@@ -202,9 +189,7 @@ Authentication occcurs in 3 steps:
202
189
  client = Xeroizer::PartnerApplication.new(
203
190
  YOUR_OAUTH_CONSUMER_KEY,
204
191
  YOUR_OAUTH_CONSUMER_SECRET,
205
- "/path/to/privatekey.pem",
206
- "/path/to/entrust-cert.pem",
207
- "/path/to/entrust-private-nopass.pem"
192
+ "/path/to/privatekey.pem"
208
193
  )
209
194
 
210
195
  # 1. Get a RequestToken from Xero. :oauth_callback is the URL the user will be redirected to
@@ -356,15 +341,23 @@ in the resulting response, including all nested XML elements.
356
341
 
357
342
  invoices = xero.Invoice.all(:where => 'FullyPaidOnDate>=DateTime.Parse("2010-01-01T00:00:00")&&FullyPaidOnDate<=DateTime.Parse("2010-01-08T00:00:00")')
358
343
 
359
- **Example 4: Retrieve all Bank Accounts:**
344
+ **Example 4: Retrieve all Invoices using Paging (batches of 100)**
345
+
346
+ invoices = xero.Invoice.find_in_batches({page_number: 1}) do |invoice_batch|
347
+ invoice_batch.each do |invoice|
348
+ ...
349
+ end
350
+ end
351
+
352
+ **Example 5: Retrieve all Bank Accounts:**
360
353
 
361
354
  accounts = xero.Account.all(:where => 'Type=="BANK"')
362
355
 
363
- **Example 5: Retrieve all DELETED or VOIDED Invoices:**
356
+ **Example 6: Retrieve all DELETED or VOIDED Invoices:**
364
357
 
365
358
  invoices = xero.Invoice.all(:where => 'Status=="VOIDED" OR Status=="DELETED"')
366
359
 
367
- **Example 6: Retrieve all contacts with specific text in the contact name:**
360
+ **Example 7: Retrieve all contacts with specific text in the contact name:**
368
361
 
369
362
  contacts = xero.Contact.all(:where => 'Name.Contains("Peter")')
370
363
  contacts = xero.Contact.all(:where => 'Name.StartsWith("Pet")')
@@ -481,7 +474,7 @@ end
481
474
  ```
482
475
 
483
476
  `batch_save` will issue one PUT request for every 2,000 unsaved records built within its block, and one
484
- POST request for evert 2,000 existing records that have been altered within its block. If any of the
477
+ POST request for every 2,000 existing records that have been altered within its block. If any of the
485
478
  unsaved records aren't valid, it'll return `false` before sending anything across the wire;
486
479
  otherwise, it returns `true`. `batch_save` takes one optional argument: the number of records to
487
480
  create/update per request. (Defaults to 2,000.)
@@ -572,7 +565,7 @@ Xero API Rate Limits
572
565
  The Xero API imposes the following limits on calls per organisation:
573
566
 
574
567
  * A limit of 60 API calls in any 60 second period
575
- * A limit of 1000 API calls in any 24 hour period
568
+ * A limit of 5000 API calls in any 24 hour period
576
569
 
577
570
  By default, the library will raise a `Xeroizer::OAuth::RateLimitExceeded`
578
571
  exception when one of these limits is exceeded.
@@ -622,7 +615,7 @@ client = Xeroizer::PublicApplication.new(YOUR_OAUTH_CONSUMER_KEY,
622
615
  HTTP Callbacks
623
616
  --------------------
624
617
 
625
- You can provide "before" and "after" callbacks which will be invoked every
618
+ You can provide "before", "after" and "around" callbacks which will be invoked every
626
619
  time Xeroizer makes an HTTP request, which is potentially useful for both
627
620
  throttling and logging:
628
621
 
@@ -631,6 +624,7 @@ Xeroizer::PublicApplication.new(
631
624
  credentials[:key], credentials[:secret],
632
625
  before_request: ->(request) { puts "Hitting this URL: #{request.url}" },
633
626
  after_request: ->(request, response) { puts "Got this response: #{response.code}" }
627
+ around_request: -> (request, &block) { puts "About to send request"; block.call; puts "After request"}
634
628
  )
635
629
  ```
636
630
 
@@ -16,10 +16,13 @@ module ClassLevelInheritableAttributes
16
16
  end
17
17
 
18
18
  def inherited(subclass)
19
+ original_verbose, $VERBOSE = $VERBOSE, nil
19
20
  @xeroizer_inheritable_attributes.each do |inheritable_attribute|
20
21
  instance_var = "@#{inheritable_attribute}"
21
22
  subclass.instance_variable_set(instance_var, instance_variable_get(instance_var))
22
23
  end
24
+ $VERBOSE = original_verbose
25
+
23
26
  end
24
27
  end
25
28
  end
data/lib/xeroizer.rb CHANGED
@@ -15,13 +15,15 @@ require 'cgi'
15
15
  $: << File.expand_path(File.dirname(__FILE__))
16
16
 
17
17
  require 'class_level_inheritable_attributes'
18
+ require 'xeroizer/exceptions'
18
19
  require 'xeroizer/oauth'
19
20
  require 'xeroizer/http_encoding_helper'
20
21
  require 'xeroizer/http'
21
- require 'xeroizer/exceptions'
22
22
 
23
23
  require 'xeroizer/record/base_model'
24
+ require 'xeroizer/record/payroll_base_model'
24
25
  require 'xeroizer/record/base'
26
+ require 'xeroizer/record/payroll_base'
25
27
  require 'xeroizer/configuration'
26
28
 
27
29
  # Include models
@@ -31,6 +33,9 @@ require 'xeroizer/models/allocation'
31
33
  require 'xeroizer/models/branding_theme'
32
34
  require 'xeroizer/models/bank_transaction'
33
35
  require 'xeroizer/models/bank_account'
36
+ require 'xeroizer/models/from_bank_account'
37
+ require 'xeroizer/models/to_bank_account'
38
+ require 'xeroizer/models/bank_transfer'
34
39
  require 'xeroizer/models/contact'
35
40
  require 'xeroizer/models/contact_group'
36
41
  require 'xeroizer/models/credit_note'
@@ -38,6 +43,7 @@ require 'xeroizer/models/currency'
38
43
  require 'xeroizer/models/employee'
39
44
  require 'xeroizer/models/expense_claim'
40
45
  require 'xeroizer/models/invoice'
46
+ require 'xeroizer/models/invoice_reminder'
41
47
  require 'xeroizer/models/item'
42
48
  require 'xeroizer/models/item_purchase_details'
43
49
  require 'xeroizer/models/item_sales_details'
@@ -50,6 +56,7 @@ require 'xeroizer/models/option'
50
56
  require 'xeroizer/models/organisation'
51
57
  require 'xeroizer/models/payment'
52
58
  require 'xeroizer/models/prepayment'
59
+ require 'xeroizer/models/overpayment'
53
60
  require 'xeroizer/models/phone'
54
61
  require 'xeroizer/models/purchase_order'
55
62
  require 'xeroizer/models/receipt'
@@ -64,6 +71,10 @@ require 'xeroizer/models/journal_line_tracking_category'
64
71
  require 'xeroizer/models/contact_sales_tracking_category'
65
72
  require 'xeroizer/models/contact_purchases_tracking_category'
66
73
 
74
+ require 'xeroizer/models/payroll/home_address'
75
+ require 'xeroizer/models/payroll/bank_account'
76
+ require 'xeroizer/models/payroll/employee'
77
+
67
78
  require 'xeroizer/report/factory'
68
79
 
69
80
  require 'xeroizer/response'
@@ -72,3 +83,4 @@ require 'xeroizer/generic_application'
72
83
  require 'xeroizer/public_application'
73
84
  require 'xeroizer/private_application'
74
85
  require 'xeroizer/partner_application'
86
+ require 'xeroizer/payroll_application'
@@ -143,7 +143,7 @@ module Xeroizer
143
143
  end
144
144
 
145
145
  def message
146
- case new_status
146
+ case @new_status
147
147
  when 'DELETED', 'VOIDED'
148
148
  unless @invoice.payments.size == 0
149
149
  "There must be no payments in this invoice to change to '#{@new_status}'"
@@ -7,7 +7,7 @@ module Xeroizer
7
7
  extend Record::ApplicationHelper
8
8
 
9
9
  attr_reader :client, :xero_url, :logger, :rate_limit_sleep, :rate_limit_max_attempts,
10
- :default_headers, :unitdp, :before_request, :after_request, :nonce_used_max_attempts
10
+ :default_headers, :unitdp, :before_request, :after_request, :around_request, :nonce_used_max_attempts
11
11
 
12
12
  extend Forwardable
13
13
  def_delegators :client, :access_token
@@ -16,6 +16,7 @@ module Xeroizer
16
16
  record :Allocation
17
17
  record :Attachment
18
18
  record :BrandingTheme
19
+ record :Balances
19
20
  record :Contact
20
21
  record :ContactGroup
21
22
  record :CreditNote
@@ -23,12 +24,15 @@ module Xeroizer
23
24
  record :Employee
24
25
  record :ExpenseClaim
25
26
  record :Invoice
27
+ record :InvoiceReminder
26
28
  record :Item
27
29
  record :Journal
30
+ record :LineItem
28
31
  record :ManualJournal
29
32
  record :Organisation
30
33
  record :Payment
31
34
  record :Prepayment
35
+ record :Overpayment
32
36
  record :PurchaseOrder
33
37
  record :Receipt
34
38
  record :RepeatingInvoice
@@ -37,6 +41,7 @@ module Xeroizer
37
41
  record :TrackingCategory
38
42
  record :TrackingCategoryChild
39
43
  record :BankTransaction
44
+ record :BankTransfer
40
45
  record :User
41
46
 
42
47
  report :AgedPayablesByContact
@@ -63,10 +68,17 @@ module Xeroizer
63
68
  @default_headers = options[:default_headers] || {}
64
69
  @before_request = options.delete(:before_request)
65
70
  @after_request = options.delete(:after_request)
71
+ @around_request = options.delete(:around_request)
66
72
  @client = OAuth.new(consumer_key, consumer_secret, options.merge({default_headers: default_headers}))
67
73
  @logger = options[:logger] || false
68
74
  @unitdp = options[:unitdp] || 2
69
75
  end
70
76
 
77
+ def payroll(options = {})
78
+ xero_client = self.clone
79
+ xero_client.xero_url = options[:xero_url] || "https://api.xero.com/payroll.xro/1.0"
80
+ @payroll ||= PayrollApplication.new(xero_client)
81
+ end
82
+
71
83
  end
72
84
  end
data/lib/xeroizer/http.rb CHANGED
@@ -14,7 +14,7 @@
14
14
 
15
15
  module Xeroizer
16
16
  module Http
17
- class BadResponse < StandardError; end
17
+ class BadResponse < XeroizerError; end
18
18
  RequestInfo = Struct.new(:url, :headers, :params, :body)
19
19
 
20
20
  ACCEPT_MIME_MAP = {
@@ -53,90 +53,100 @@ module Xeroizer
53
53
 
54
54
  private
55
55
 
56
- def http_request(client, method, url, body, params = {})
57
- # headers = {'Accept-Encoding' => 'gzip, deflate'}
56
+ def http_request(client, method, url, body, params = {})
57
+ # headers = {'Accept-Encoding' => 'gzip, deflate'}
58
58
 
59
- headers = self.default_headers.merge({ 'charset' => 'utf-8' })
59
+ headers = self.default_headers.merge({ 'charset' => 'utf-8' })
60
60
 
61
- # include the unitdp query string parameter
62
- params.merge!(unitdp_param(url))
61
+ # include the unitdp query string parameter
62
+ params.merge!(unitdp_param(url))
63
63
 
64
- if method != :get
65
- headers['Content-Type'] ||= "application/x-www-form-urlencoded"
66
- end
64
+ if method != :get
65
+ headers['Content-Type'] ||= "application/x-www-form-urlencoded"
66
+ end
67
67
 
68
- content_type = params.delete(:content_type)
69
- headers['Content-Type'] = content_type if content_type
68
+ content_type = params.delete(:content_type)
69
+ headers['Content-Type'] = content_type if content_type
70
70
 
71
- # HAX. Xero completely misuse the If-Modified-Since HTTP header.
72
- headers['If-Modified-Since'] = params.delete(:ModifiedAfter).utc.strftime("%Y-%m-%dT%H:%M:%S") if params[:ModifiedAfter]
71
+ # HAX. Xero completely misuse the If-Modified-Since HTTP header.
72
+ headers['If-Modified-Since'] = params.delete(:ModifiedAfter).utc.strftime("%Y-%m-%dT%H:%M:%S") if params[:ModifiedAfter]
73
73
 
74
- # Allow 'Accept' header to be specified with :accept parameter.
75
- # Valid values are :pdf or :json.
76
- if params[:response]
77
- response_type = params.delete(:response)
78
- headers['Accept'] = case response_type
79
- when Symbol then ACCEPT_MIME_MAP[response_type]
80
- else response_type
81
- end
74
+ # Allow 'Accept' header to be specified with :accept parameter.
75
+ # Valid values are :pdf or :json.
76
+ if params[:response]
77
+ response_type = params.delete(:response)
78
+ headers['Accept'] = case response_type
79
+ when Symbol then ACCEPT_MIME_MAP[response_type]
80
+ else response_type
82
81
  end
82
+ end
83
83
 
84
- if params.any?
85
- url += "?" + params.map {|key,value| "#{CGI.escape(key.to_s)}=#{CGI.escape(value.to_s)}"}.join("&")
86
- end
84
+ if params.any?
85
+ url += "?" + params.map {|key, value| "#{CGI.escape(key.to_s)}=#{CGI.escape(value.to_s)}"}.join("&")
86
+ end
87
87
 
88
- uri = URI.parse(url)
88
+ uri = URI.parse(url)
89
89
 
90
- attempts = 0
90
+ attempts = 0
91
91
 
92
- request_info = RequestInfo.new(url, headers, params, body)
93
- before_request.call(request_info) if before_request
92
+ request_info = RequestInfo.new(url, headers, params, body)
93
+ before_request.call(request_info) if before_request
94
94
 
95
- begin
96
- attempts += 1
97
- logger.info("XeroGateway Request: #{method.to_s.upcase} #{uri.request_uri}") if self.logger
95
+ begin
96
+ attempts += 1
97
+ logger.info("XeroGateway Request: #{method.to_s.upcase} #{uri.request_uri}") if self.logger
98
98
 
99
- raw_body = params.delete(:raw_body) ? body : {:xml => body}
99
+ raw_body = params.delete(:raw_body) ? body : {:xml => body}
100
100
 
101
- response = case method
101
+ response = with_around_request(request_info) do
102
+ case method
102
103
  when :get then client.get(uri.request_uri, headers)
103
104
  when :post then client.post(uri.request_uri, raw_body, headers)
104
105
  when :put then client.put(uri.request_uri, raw_body, headers)
105
106
  end
107
+ end
106
108
 
107
- log_response(response, uri)
108
- after_request.call(request_info, response) if after_request
109
-
110
- case response.code.to_i
111
- when 200
112
- response.plain_body
113
- when 400
114
- handle_error!(response, body)
115
- when 401
116
- handle_oauth_error!(response)
117
- when 404
118
- handle_object_not_found!(response, url)
119
- when 503
120
- handle_oauth_error!(response)
121
- else
122
- handle_unknown_response_error!(response)
123
- end
124
- rescue Xeroizer::OAuth::NonceUsed => exception
125
- raise if attempts > nonce_used_max_attempts
126
- logger.info("Nonce used: " + exception.to_s) if self.logger
127
- sleep_for(1)
128
- retry
129
- rescue Xeroizer::OAuth::RateLimitExceeded
130
- if self.rate_limit_sleep
131
- raise if attempts > rate_limit_max_attempts
132
- logger.info("Rate limit exceeded, retrying") if self.logger
133
- sleep_for(self.rate_limit_sleep)
134
- retry
109
+ log_response(response, uri)
110
+ after_request.call(request_info, response) if after_request
111
+
112
+ case response.code.to_i
113
+ when 200
114
+ response.plain_body
115
+ when 400
116
+ handle_error!(response, body)
117
+ when 401
118
+ handle_oauth_error!(response)
119
+ when 404
120
+ handle_object_not_found!(response, url)
121
+ when 503
122
+ handle_oauth_error!(response)
135
123
  else
136
- raise
137
- end
124
+ handle_unknown_response_error!(response)
125
+ end
126
+ rescue Xeroizer::OAuth::NonceUsed => exception
127
+ raise if attempts > nonce_used_max_attempts
128
+ logger.info("Nonce used: " + exception.to_s) if self.logger
129
+ sleep_for(1)
130
+ retry
131
+ rescue Xeroizer::OAuth::RateLimitExceeded
132
+ if self.rate_limit_sleep
133
+ raise if attempts > rate_limit_max_attempts
134
+ logger.info("Rate limit exceeded, retrying") if self.logger
135
+ sleep_for(self.rate_limit_sleep)
136
+ retry
137
+ else
138
+ raise
138
139
  end
139
140
  end
141
+ end
142
+
143
+ def with_around_request(request, &block)
144
+ if around_request
145
+ around_request.call(request, &block)
146
+ else
147
+ block.call
148
+ end
149
+ end
140
150
 
141
151
  def log_response(response, uri)
142
152
  if self.logger
@@ -148,78 +158,76 @@ module Xeroizer
148
158
  end
149
159
 
150
160
  def handle_oauth_error!(response)
151
- error_details = CGI.parse(response.plain_body)
152
- description = error_details["oauth_problem_advice"].first
153
- problem = error_details["oauth_problem"].first
154
-
155
- # see http://oauth.pbworks.com/ProblemReporting
156
- # In addition to token_expired and token_rejected, Xero also returns
157
- # 'rate limit exceeded' when more than 60 requests have been made in
158
- # a second.
159
- if problem
160
- case (problem)
161
- when "token_expired" then raise OAuth::TokenExpired.new(description)
162
- when "token_rejected" then raise OAuth::TokenInvalid.new(description)
163
- when "rate limit exceeded" then raise OAuth::RateLimitExceeded.new(description)
164
- when "consumer_key_unknown" then raise OAuth::ConsumerKeyUnknown.new(description)
165
- when "nonce_used" then raise OAuth::NonceUsed.new(description)
166
- else raise OAuth::UnknownError.new(problem + ':' + description)
167
- end
168
- else
169
- raise OAuth::UnknownError.new("Xero API may be down or the way OAuth errors are provided by Xero may have changed.")
161
+ error_details = CGI.parse(response.plain_body)
162
+ description = error_details["oauth_problem_advice"].first
163
+ problem = error_details["oauth_problem"].first
164
+
165
+ # see http://oauth.pbworks.com/ProblemReporting
166
+ # In addition to token_expired and token_rejected, Xero also returns
167
+ # 'rate limit exceeded' when more than 60 requests have been made in
168
+ # a second.
169
+ if problem
170
+ case problem
171
+ when "token_expired" then raise OAuth::TokenExpired.new(description)
172
+ when "token_rejected" then raise OAuth::TokenInvalid.new(description)
173
+ when "rate limit exceeded" then raise OAuth::RateLimitExceeded.new(description)
174
+ when "consumer_key_unknown" then raise OAuth::ConsumerKeyUnknown.new(description)
175
+ when "nonce_used" then raise OAuth::NonceUsed.new(description)
176
+ when "organisation offline" then raise OAuth::OrganisationOffline.new(description)
177
+ else raise OAuth::UnknownError.new(problem + ':' + description)
170
178
  end
179
+ else
180
+ raise OAuth::UnknownError.new("Xero API may be down or the way OAuth errors are provided by Xero may have changed.")
171
181
  end
182
+ end
172
183
 
173
- def handle_error!(response, request_body)
174
-
175
- raw_response = response.plain_body
176
-
177
- # XeroGenericApplication API Exceptions *claim* to be UTF-16 encoded, but fail REXML/Iconv parsing...
178
- # So let's ignore that :)
179
- raw_response.gsub! '<?xml version="1.0" encoding="utf-16"?>', ''
180
-
181
- # doc = REXML::Document.new(raw_response, :ignore_whitespace_nodes => :all)
182
- doc = Nokogiri::XML(raw_response)
184
+ def handle_error!(response, request_body)
183
185
 
184
- if doc && doc.root && doc.root.name == "ApiException"
186
+ raw_response = response.plain_body
185
187
 
186
- raise ApiException.new(doc.root.xpath("Type").text,
187
- doc.root.xpath("Message").text,
188
- raw_response,
189
- doc,
190
- request_body)
188
+ # XeroGenericApplication API Exceptions *claim* to be UTF-16 encoded, but fail REXML/Iconv parsing...
189
+ # So let's ignore that :)
190
+ raw_response.gsub! '<?xml version="1.0" encoding="utf-16"?>', ''
191
191
 
192
- else
192
+ # doc = REXML::Document.new(raw_response, :ignore_whitespace_nodes => :all)
193
+ doc = Nokogiri::XML(raw_response)
193
194
 
194
- raise BadResponse.new("Unparseable 400 Response: #{raw_response}")
195
+ if doc && doc.root && doc.root.name == "ApiException"
195
196
 
196
- end
197
+ raise ApiException.new(doc.root.xpath("Type").text,
198
+ doc.root.xpath("Message").text,
199
+ raw_response,
200
+ doc,
201
+ request_body)
197
202
 
203
+ else
204
+ raise BadResponse.new("Unparseable 400 Response: #{raw_response}")
198
205
  end
206
+ end
199
207
 
200
- def handle_object_not_found!(response, request_url)
201
- case(request_url)
202
- when /Invoices/ then raise InvoiceNotFoundError.new("Invoice not found in Xero.")
203
- when /CreditNotes/ then raise CreditNoteNotFoundError.new("Credit Note not found in Xero.")
204
- else raise ObjectNotFound.new(request_url)
205
- end
208
+ def handle_object_not_found!(response, request_url)
209
+ case request_url
210
+ when /Invoices/ then raise InvoiceNotFoundError.new("Invoice not found in Xero.")
211
+ when /CreditNotes/ then raise CreditNoteNotFoundError.new("Credit Note not found in Xero.")
212
+ else raise ObjectNotFound.new(request_url)
206
213
  end
214
+ end
207
215
 
208
- def handle_unknown_response_error!(response)
209
- raise BadResponse.new("Unknown response code: #{response.code.to_i}")
210
- end
216
+ def handle_unknown_response_error!(response)
217
+ raise BadResponse.new("Unknown response code: #{response.code.to_i}")
218
+ end
211
219
 
212
- def sleep_for(seconds = 1)
213
- sleep seconds
214
- end
220
+ def sleep_for(seconds = 1)
221
+ sleep seconds
222
+ end
215
223
 
216
- # unitdp query string parameter to be added to request params
217
- # when the application option has been set and the model has line items
218
- # http://developer.xero.com/documentation/advanced-docs/rounding-in-xero/#unitamount
219
- def unitdp_param(request_url)
220
- models = [/Invoices/, /CreditNotes/, /BankTransactions/, /Receipts/]
221
- self.unitdp == 4 && models.any?{ |m| request_url =~ m } ? {:unitdp => 4} : {}
222
- end
224
+ # unitdp query string parameter to be added to request params
225
+ # when the application option has been set and the model has line items
226
+ # http://developer.xero.com/documentation/advanced-docs/rounding-in-xero/#unitamount
227
+ def unitdp_param(request_url)
228
+ models = [/Invoices/, /CreditNotes/, /BankTransactions/, /Receipts/]
229
+ self.unitdp == 4 && models.any?{ |m| request_url =~ m } ? {:unitdp => 4} : {}
230
+ end
223
231
 
224
232
  end
225
233
  end