quickbooks-ruby 0.6.7 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/quickbooks-ruby.rb +17 -1
- data/lib/quickbooks/faraday/middleware/gzip.rb +72 -0
- data/lib/quickbooks/model/base_model.rb +2 -1
- data/lib/quickbooks/model/group_line_detail.rb +2 -1
- data/lib/quickbooks/model/invoice.rb +2 -0
- data/lib/quickbooks/model/line.rb +11 -0
- data/lib/quickbooks/model/time_activity.rb +1 -0
- data/lib/quickbooks/service/access_token.rb +18 -9
- data/lib/quickbooks/service/base_service.rb +97 -24
- data/lib/quickbooks/service/invoice.rb +15 -1
- data/lib/quickbooks/service/responses/methods.rb +17 -0
- data/lib/quickbooks/service/responses/oauth1_http_response.rb +42 -0
- data/lib/quickbooks/service/responses/oauth2_http_response.rb +43 -0
- data/lib/quickbooks/service/responses/oauth_http_response.rb +21 -0
- data/lib/quickbooks/service/service_crud.rb +4 -0
- data/lib/quickbooks/service/upload.rb +5 -1
- data/lib/quickbooks/util/multipart.rb +11 -0
- data/lib/quickbooks/version.rb +1 -1
- metadata +44 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d1f6e851cfb68ae7f6acf08a355b9d3e917d92f98a0bac9c53098f1a4c8b2f6f
|
4
|
+
data.tar.gz: 9db0566d15faef2b617f35c288eee7762ef0e7714586f6c6b9b6dd5add1cb3b3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 61f501677c4c382b9d4e1200158dc9daac57e12d51274c7eeeabb4415d41bec14a80e32b2cdea40923dcc962aac9e4593bbc83f60f389552aaac3301e241e6c2
|
7
|
+
data.tar.gz: 966a4d88b2f45eaa8d6bea233cca742b5d3cdeee1b9340f0b060ea03efade653b432d38e222fd47a13057f502991c477946347dfed4f155868756060260229e1
|
data/lib/quickbooks-ruby.rb
CHANGED
@@ -7,12 +7,20 @@ require 'uri'
|
|
7
7
|
require 'date'
|
8
8
|
require 'forwardable'
|
9
9
|
require 'oauth'
|
10
|
+
require 'oauth2'
|
10
11
|
require 'net/http/post/multipart'
|
11
12
|
require 'quickbooks/util/collection'
|
12
13
|
require 'quickbooks/util/logging'
|
13
14
|
require 'quickbooks/util/http_encoding_helper'
|
14
15
|
require 'quickbooks/util/name_entity'
|
15
16
|
require 'quickbooks/util/query_builder'
|
17
|
+
require 'quickbooks/faraday/middleware/gzip'
|
18
|
+
|
19
|
+
#== OAuth Responses
|
20
|
+
require 'quickbooks/service/responses/oauth_http_response'
|
21
|
+
require 'quickbooks/service/responses/methods'
|
22
|
+
require 'quickbooks/service/responses/oauth1_http_response'
|
23
|
+
require 'quickbooks/service/responses/oauth2_http_response'
|
16
24
|
|
17
25
|
#== Models
|
18
26
|
require 'quickbooks/model/definition'
|
@@ -176,9 +184,11 @@ require 'quickbooks/service/transfer'
|
|
176
184
|
require 'quickbooks/service/change_data_capture'
|
177
185
|
require 'quickbooks/service/refund_receipt_change'
|
178
186
|
|
187
|
+
# Register Faraday Middleware
|
188
|
+
Faraday::Middleware.register_middleware :gzip => lambda { Gzip }
|
189
|
+
|
179
190
|
module Quickbooks
|
180
191
|
@@sandbox_mode = false
|
181
|
-
|
182
192
|
@@logger = nil
|
183
193
|
|
184
194
|
class << self
|
@@ -239,4 +249,10 @@ module Quickbooks
|
|
239
249
|
end
|
240
250
|
end
|
241
251
|
|
252
|
+
class InvalidOauthAccessTokenObject < StandardError
|
253
|
+
def initialize(access_token)
|
254
|
+
super("Expected access token to be an instance of OAuth::AccessToken or OAuth2::AccessToken, got #{access_token.class}.")
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
242
258
|
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# https://github.com/lostisland/faraday_middleware/blob/master/lib/faraday_middleware/gzip.rb
|
2
|
+
|
3
|
+
require 'faraday'
|
4
|
+
|
5
|
+
# Middleware to automatically decompress response bodies. If the
|
6
|
+
# "Accept-Encoding" header wasn't set in the request, this sets it to
|
7
|
+
# "gzip,deflate" and appropriately handles the compressed response from the
|
8
|
+
# server. This resembles what Ruby 1.9+ does internally in Net::HTTP#get.
|
9
|
+
#
|
10
|
+
# This middleware is NOT necessary when these adapters are used:
|
11
|
+
# - net_http on Ruby 1.9+
|
12
|
+
# - net_http_persistent on Ruby 2.0+
|
13
|
+
# - em_http
|
14
|
+
class Gzip < Faraday::Middleware
|
15
|
+
dependency 'zlib'
|
16
|
+
|
17
|
+
ACCEPT_ENCODING = 'Accept-Encoding'.freeze
|
18
|
+
CONTENT_ENCODING = 'Content-Encoding'.freeze
|
19
|
+
CONTENT_LENGTH = 'Content-Length'.freeze
|
20
|
+
SUPPORTED_ENCODINGS = 'gzip,deflate,br'.freeze
|
21
|
+
RUBY_ENCODING = '1.9'.respond_to?(:force_encoding)
|
22
|
+
|
23
|
+
def call(env)
|
24
|
+
env[:request_headers][ACCEPT_ENCODING] ||= SUPPORTED_ENCODINGS
|
25
|
+
@app.call(env).on_complete do |response_env|
|
26
|
+
case response_env[:response_headers][CONTENT_ENCODING]
|
27
|
+
when 'gzip'
|
28
|
+
reset_body(response_env, &method(:uncompress_gzip))
|
29
|
+
when 'deflate'
|
30
|
+
reset_body(response_env, &method(:inflate))
|
31
|
+
when 'br'
|
32
|
+
reset_body(response_env, &method(:brotli_inflate))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def reset_body(env)
|
38
|
+
env[:body] = yield(env[:body])
|
39
|
+
env[:response_headers].delete(CONTENT_ENCODING)
|
40
|
+
env[:response_headers][CONTENT_LENGTH] = env[:body].length
|
41
|
+
end
|
42
|
+
|
43
|
+
def uncompress_gzip(body)
|
44
|
+
io = StringIO.new(body)
|
45
|
+
gzip_reader = if RUBY_ENCODING
|
46
|
+
Zlib::GzipReader.new(io, :encoding => 'ASCII-8BIT')
|
47
|
+
else
|
48
|
+
Zlib::GzipReader.new(io)
|
49
|
+
end
|
50
|
+
gzip_reader.read
|
51
|
+
end
|
52
|
+
|
53
|
+
def inflate(body)
|
54
|
+
# Inflate as a DEFLATE (RFC 1950+RFC 1951) stream
|
55
|
+
Zlib::Inflate.inflate(body)
|
56
|
+
rescue Zlib::DataError
|
57
|
+
# Fall back to inflating as a "raw" deflate stream which
|
58
|
+
# Microsoft servers return
|
59
|
+
inflate = Zlib::Inflate.new(-Zlib::MAX_WBITS)
|
60
|
+
begin
|
61
|
+
inflate.inflate(body)
|
62
|
+
ensure
|
63
|
+
inflate.close
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def brotli_inflate(body)
|
68
|
+
self.class.dependency 'brotli'
|
69
|
+
|
70
|
+
Brotli.inflate(body)
|
71
|
+
end
|
72
|
+
end
|
@@ -2,6 +2,7 @@ module Quickbooks
|
|
2
2
|
module Model
|
3
3
|
class BaseModel
|
4
4
|
include Definition
|
5
|
+
include ActiveModel::AttributeMethods
|
5
6
|
include ActiveModel::Validations
|
6
7
|
include Validator
|
7
8
|
include ROXML
|
@@ -54,7 +55,7 @@ module Quickbooks
|
|
54
55
|
def inspect
|
55
56
|
# it would be nice if we could inspect all the children,
|
56
57
|
# but it's likely to blow the stack in some cases
|
57
|
-
"#<#{self.class} " +
|
58
|
+
"#<#{self.class} " +
|
58
59
|
"#{attributes.map{|k,v| "#{k}: #{v.nil? ? 'nil' : v.to_s }"}.join ", "}>"
|
59
60
|
end
|
60
61
|
class << self
|
@@ -3,7 +3,8 @@ module Quickbooks
|
|
3
3
|
class GroupLineDetail < BaseModel
|
4
4
|
include HasLineItems
|
5
5
|
|
6
|
-
xml_accessor :
|
6
|
+
xml_accessor :id, :from => 'Id'
|
7
|
+
xml_accessor :group_item_ref, :from => 'GroupItemRef', :as => BaseReference
|
7
8
|
xml_accessor :quantity, :from => 'Quantity', :as => BigDecimal, :to_xml => to_xml_big_decimal
|
8
9
|
xml_accessor :line_items, :from => 'Line', :as => [Line]
|
9
10
|
|
@@ -17,6 +17,7 @@ module Quickbooks
|
|
17
17
|
XML_COLLECTION_NODE = "Invoice"
|
18
18
|
XML_NODE = "Invoice"
|
19
19
|
EMAIL_STATUS_NEED_TO_SEND = 'NeedToSend'
|
20
|
+
MINORVERSION = 37
|
20
21
|
|
21
22
|
xml_accessor :id, :from => 'Id'
|
22
23
|
xml_accessor :sync_token, :from => 'SyncToken', :as => Integer
|
@@ -24,6 +25,7 @@ module Quickbooks
|
|
24
25
|
xml_accessor :custom_fields, :from => 'CustomField', :as => [CustomField]
|
25
26
|
xml_accessor :auto_doc_number, :from => 'AutoDocNumber' # See auto_doc_number! method below for usage
|
26
27
|
xml_accessor :doc_number, :from => 'DocNumber'
|
28
|
+
xml_accessor :invoice_link, :from => 'InvoiceLink'
|
27
29
|
xml_accessor :txn_date, :from => 'TxnDate', :as => Date
|
28
30
|
xml_accessor :currency_ref, :from => 'CurrencyRef', :as => BaseReference
|
29
31
|
xml_accessor :exchange_rate, :from => 'ExchangeRate', :as => BigDecimal, :to_xml => to_xml_big_decimal
|
@@ -1,12 +1,15 @@
|
|
1
1
|
module Quickbooks
|
2
2
|
module Model
|
3
3
|
class Line < BaseModel
|
4
|
+
require 'quickbooks/model/group_line_detail'
|
5
|
+
|
4
6
|
#== Constants
|
5
7
|
SALES_ITEM_LINE_DETAIL = 'SalesItemLineDetail'
|
6
8
|
SUB_TOTAL_LINE_DETAIL = 'SubTotalLineDetail'
|
7
9
|
PAYMENT_LINE_DETAIL = 'PaymentLineDetail'
|
8
10
|
DISCOUNT_LINE_DETAIL = 'DiscountLineDetail'
|
9
11
|
JOURNAL_ENTRY_LINE_DETAIL = 'JournalEntryLineDetail'
|
12
|
+
GROUP_LINE_DETAIL = 'GroupLineDetail'
|
10
13
|
|
11
14
|
xml_accessor :id, :from => 'Id'
|
12
15
|
xml_accessor :line_num, :from => 'LineNum', :as => Integer
|
@@ -22,6 +25,7 @@ module Quickbooks
|
|
22
25
|
xml_accessor :payment_line_detail, :from => 'PaymentLineDetail', :as => PaymentLineDetail
|
23
26
|
xml_accessor :discount_line_detail, :from => 'DiscountLineDetail', :as => DiscountOverride
|
24
27
|
xml_accessor :journal_entry_line_detail, :from => 'JournalEntryLineDetail', :as => JournalEntryLineDetail
|
28
|
+
xml_accessor :group_line_detail, :from => 'GroupLineDetail', :as => GroupLineDetail
|
25
29
|
|
26
30
|
def initialize(*args)
|
27
31
|
self.linked_transactions ||= []
|
@@ -73,6 +77,13 @@ module Quickbooks
|
|
73
77
|
yield self.journal_entry_line_detail if block_given?
|
74
78
|
end
|
75
79
|
|
80
|
+
def group_line!
|
81
|
+
self.detail_type = GROUP_LINE_DETAIL
|
82
|
+
self.group_line_detail = GroupLineDetail.new
|
83
|
+
|
84
|
+
yield self.group_line_detail if block_given?
|
85
|
+
end
|
86
|
+
|
76
87
|
private
|
77
88
|
|
78
89
|
def update_linked_transactions(txn_ids, txn_type)
|
@@ -43,6 +43,7 @@ module Quickbooks
|
|
43
43
|
validates_inclusion_of :name_of, :in => NAMEOF_OPTIONS
|
44
44
|
validate :existence_of_employee_ref, :if => Proc.new { |ta| ta.name_of == "Employee" }
|
45
45
|
validate :existence_of_vendor_ref, :if => Proc.new { |ta| ta.name_of == "Vendor" }
|
46
|
+
validates :description, length: { maximum: 4000 }
|
46
47
|
|
47
48
|
def existence_of_employee_ref
|
48
49
|
if employee_ref.nil? || (employee_ref && employee_ref.value == 0)
|
@@ -3,7 +3,8 @@ module Quickbooks
|
|
3
3
|
class AccessToken < BaseService
|
4
4
|
|
5
5
|
RENEW_URL = "https://appcenter.intuit.com/api/v1/connection/reconnect"
|
6
|
-
|
6
|
+
DISCONNECT_URL_OAUTH1 = "https://appcenter.intuit.com/api/v1/connection/disconnect"
|
7
|
+
DISCONNECT_URL_OAUTH2 = "https://developer.api.intuit.com/v2/oauth2/tokens/revoke"
|
7
8
|
|
8
9
|
# https://developer.intuit.com/docs/0025_quickbooksapi/0053_auth_auth/oauth_management_api#Reconnect
|
9
10
|
def renew
|
@@ -21,16 +22,24 @@ module Quickbooks
|
|
21
22
|
|
22
23
|
# https://developer.intuit.com/docs/0025_quickbooksapi/0053_auth_auth/oauth_management_api#Disconnect
|
23
24
|
def disconnect
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
if code == 200
|
29
|
-
result = Quickbooks::Model::AccessTokenResponse.from_xml(response.plain_body)
|
25
|
+
if oauth_v1?
|
26
|
+
response = do_http_get(DISCONNECT_URL_OAUTH1)
|
27
|
+
if response && response.code.to_i == 200
|
28
|
+
Quickbooks::Model::AccessTokenResponse.from_xml(response.plain_body)
|
30
29
|
end
|
31
|
-
|
30
|
+
elsif oauth_v2?
|
31
|
+
conn = Faraday.new
|
32
|
+
conn.basic_auth oauth.client.id, oauth.client.secret
|
33
|
+
response = conn.post(DISCONNECT_URL_OAUTH2, token: oauth.refresh_token || oauth.token)
|
32
34
|
|
33
|
-
|
35
|
+
if response.success?
|
36
|
+
Quickbooks::Model::AccessTokenResponse.new(error_code: "0")
|
37
|
+
else
|
38
|
+
Quickbooks::Model::AccessTokenResponse.new(
|
39
|
+
error_code: response.status.to_s, error_message: response.reason_phrase
|
40
|
+
)
|
41
|
+
end
|
42
|
+
end
|
34
43
|
end
|
35
44
|
|
36
45
|
end
|
@@ -25,6 +25,7 @@ module Quickbooks
|
|
25
25
|
|
26
26
|
def access_token=(token)
|
27
27
|
@oauth = token
|
28
|
+
rebuild_connection!
|
28
29
|
end
|
29
30
|
|
30
31
|
def company_id=(company_id)
|
@@ -36,6 +37,27 @@ module Quickbooks
|
|
36
37
|
@company_id = company_id
|
37
38
|
end
|
38
39
|
|
40
|
+
def oauth_v1?
|
41
|
+
@oauth.is_a? OAuth::AccessToken
|
42
|
+
end
|
43
|
+
|
44
|
+
def oauth_v2?
|
45
|
+
@oauth.is_a? OAuth2::AccessToken
|
46
|
+
end
|
47
|
+
|
48
|
+
# [OAuth2] The default Faraday connection does not have gzip or multipart support.
|
49
|
+
# We need to reset the existing connection and build a new one.
|
50
|
+
def rebuild_connection!
|
51
|
+
return unless oauth_v2?
|
52
|
+
@oauth.client.connection = nil
|
53
|
+
@oauth.client.connection.build do |builder|
|
54
|
+
builder.use :gzip
|
55
|
+
builder.request :multipart
|
56
|
+
builder.request :url_encoded
|
57
|
+
builder.adapter :net_http
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
39
61
|
def url_for_resource(resource)
|
40
62
|
"#{url_for_base}/#{resource}"
|
41
63
|
end
|
@@ -61,7 +83,7 @@ module Quickbooks
|
|
61
83
|
query ||= default_model_query
|
62
84
|
query = "#{query} STARTPOSITION #{start_position} MAXRESULTS #{max_results}"
|
63
85
|
|
64
|
-
"#{url_for_base}/query?query=#{
|
86
|
+
"#{url_for_base}/query?query=#{CGI.escape(query)}"
|
65
87
|
end
|
66
88
|
|
67
89
|
private
|
@@ -189,7 +211,8 @@ module Quickbooks
|
|
189
211
|
unless headers.has_key?('Accept-Encoding')
|
190
212
|
headers['Accept-Encoding'] = HTTP_ACCEPT_ENCODING
|
191
213
|
end
|
192
|
-
|
214
|
+
raw_response = oauth_get(url, headers)
|
215
|
+
Quickbooks::Service::Responses::OAuthHttpResponse.wrap(raw_response)
|
193
216
|
end
|
194
217
|
|
195
218
|
def do_http_file_upload(uploadIO, url, metadata = nil)
|
@@ -229,21 +252,48 @@ module Quickbooks
|
|
229
252
|
log_request_body(body)
|
230
253
|
log "REQUEST HEADERS = #{headers.inspect}"
|
231
254
|
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
255
|
+
raw_response = case method
|
256
|
+
when :get
|
257
|
+
oauth_get(url, headers)
|
258
|
+
when :post
|
259
|
+
oauth_post(url, body, headers)
|
260
|
+
when :upload
|
261
|
+
oauth_post_with_multipart(url, body, headers)
|
262
|
+
else
|
263
|
+
raise "Do not know how to perform that HTTP operation"
|
264
|
+
end
|
242
265
|
|
243
|
-
|
244
|
-
|
266
|
+
response = Quickbooks::Service::Responses::OAuthHttpResponse.wrap(raw_response)
|
267
|
+
check_response(response, request: body)
|
268
|
+
end
|
269
|
+
|
270
|
+
def oauth_get(url, headers)
|
271
|
+
if oauth_v1?
|
272
|
+
@oauth.get(url, headers)
|
273
|
+
elsif oauth_v2?
|
274
|
+
@oauth.get(url, headers: headers, raise_errors: false)
|
245
275
|
else
|
246
|
-
|
276
|
+
raise InvalidOauthAccessTokenObject.new(@oauth)
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
def oauth_post(url, body, headers)
|
281
|
+
if oauth_v1?
|
282
|
+
@oauth.post(url, body, headers)
|
283
|
+
elsif oauth_v2?
|
284
|
+
@oauth.post(url, headers: headers, body: body, raise_errors: false)
|
285
|
+
else
|
286
|
+
raise InvalidOauthAccessTokenObject.new(@oauth)
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
def oauth_post_with_multipart(url, body, headers)
|
291
|
+
if oauth_v1?
|
292
|
+
@oauth.post_with_multipart(url, body, headers)
|
293
|
+
elsif oauth_v2?
|
294
|
+
@oauth.post_with_multipart(url, headers: headers, body: body, raise_errors: false)
|
295
|
+
else
|
296
|
+
raise InvalidOauthAccessTokenObject.new(@oauth)
|
247
297
|
end
|
248
298
|
end
|
249
299
|
|
@@ -258,12 +308,11 @@ module Quickbooks
|
|
258
308
|
end
|
259
309
|
|
260
310
|
def check_response(response, options = {})
|
261
|
-
log "------ RESPONSE_HEADERS -----"
|
262
|
-
if log?
|
263
|
-
response.each_header {|h| log "#{h}: #{response[h]}"}
|
264
|
-
end
|
265
311
|
log "------ QUICKBOOKS-RUBY RESPONSE ------"
|
266
312
|
log "RESPONSE CODE = #{response.code}"
|
313
|
+
if response.respond_to?(:headers)
|
314
|
+
log "RESPONSE HEADERS = #{response.headers}"
|
315
|
+
end
|
267
316
|
log_response_body(response)
|
268
317
|
status = response.code.to_i
|
269
318
|
case status
|
@@ -293,7 +342,7 @@ module Quickbooks
|
|
293
342
|
when 429
|
294
343
|
message = parse_intuit_error[:message]
|
295
344
|
raise Quickbooks::TooManyRequests, message
|
296
|
-
when
|
345
|
+
when 503, 504
|
297
346
|
raise Quickbooks::ServiceUnavailable
|
298
347
|
else
|
299
348
|
raise "HTTP Error Code: #{status}, Msg: #{response.plain_body}"
|
@@ -305,6 +354,8 @@ module Quickbooks
|
|
305
354
|
if is_json?
|
306
355
|
log ">>>>#{response.plain_body.inspect}"
|
307
356
|
parse_json(response.plain_body)
|
357
|
+
elsif is_pdf?
|
358
|
+
log("BODY is a PDF : not dumping")
|
308
359
|
else
|
309
360
|
log(log_xml(response.plain_body))
|
310
361
|
parse_xml(response.plain_body)
|
@@ -315,8 +366,26 @@ module Quickbooks
|
|
315
366
|
log "REQUEST BODY:"
|
316
367
|
if is_json?
|
317
368
|
log(body.inspect)
|
369
|
+
elsif is_pdf?
|
370
|
+
log("BODY is a PDF : not dumping")
|
318
371
|
else
|
319
|
-
|
372
|
+
#multipart request for uploads arrive here in a Hash with UploadIO vals
|
373
|
+
if body.is_a?(Hash)
|
374
|
+
body.each do |k,v|
|
375
|
+
log('BODY PART:')
|
376
|
+
val_content = v.inspect
|
377
|
+
if v.is_a?(UploadIO)
|
378
|
+
if v.content_type == 'application/xml'
|
379
|
+
if v.io.is_a?(StringIO)
|
380
|
+
val_content = log_xml(v.io.string)
|
381
|
+
end
|
382
|
+
end
|
383
|
+
end
|
384
|
+
log("#{k}: #{val_content}")
|
385
|
+
end
|
386
|
+
else
|
387
|
+
log(log_xml(body))
|
388
|
+
end
|
320
389
|
end
|
321
390
|
end
|
322
391
|
|
@@ -335,9 +404,13 @@ module Quickbooks
|
|
335
404
|
end
|
336
405
|
|
337
406
|
def response_is_error?
|
338
|
-
|
339
|
-
|
340
|
-
|
407
|
+
begin
|
408
|
+
@last_response_xml.xpath("//xmlns:IntuitResponse/xmlns:Fault")[0] != nil
|
409
|
+
rescue Nokogiri::XML::XPath::SyntaxError => exception
|
410
|
+
#puts @last_response_xml.to_xml.to_s
|
411
|
+
#puts "WTF: #{exception.inspect}:#{exception.backtrace.join("\n")}"
|
412
|
+
true
|
413
|
+
end
|
341
414
|
end
|
342
415
|
|
343
416
|
def parse_intuit_error
|
@@ -6,10 +6,24 @@ module Quickbooks
|
|
6
6
|
delete_by_query_string(invoice)
|
7
7
|
end
|
8
8
|
|
9
|
+
def url_for_resource(resource)
|
10
|
+
url = super(resource)
|
11
|
+
end
|
12
|
+
|
13
|
+
def fetch_by_id(id, params = {})
|
14
|
+
url = "#{url_for_base}/invoice/#{id}?minorversion=#{Quickbooks::Model::Invoice::MINORVERSION}"
|
15
|
+
fetch_object(model, url, params)
|
16
|
+
end
|
17
|
+
|
18
|
+
def url_for_query(query = nil, start_position = 1, max_results = 20, options = {})
|
19
|
+
url = super(query, start_position, max_results, options)
|
20
|
+
"#{url}&minorversion=#{Quickbooks::Model::Invoice::MINORVERSION}"
|
21
|
+
end
|
22
|
+
|
9
23
|
def send(invoice, email_address=nil)
|
10
24
|
query = email_address.present? ? "?sendTo=#{email_address}" : ""
|
11
25
|
url = "#{url_for_resource(model::REST_RESOURCE)}/#{invoice.id}/send#{query}"
|
12
|
-
response = do_http_post(url,{})
|
26
|
+
response = do_http_post(url, "", {}, { 'Content-Type' => 'application/octet-stream' })
|
13
27
|
if response.code.to_i == 200
|
14
28
|
model.from_xml(parse_singular_entity_response(model, response.plain_body))
|
15
29
|
else
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Quickbooks
|
2
|
+
module Service
|
3
|
+
module Responses
|
4
|
+
|
5
|
+
class OAuth1HttpResponse < OAuthHttpResponse
|
6
|
+
|
7
|
+
attr_accessor :real_response
|
8
|
+
|
9
|
+
# net/http response
|
10
|
+
def initialize(response)
|
11
|
+
@real_response = response
|
12
|
+
end
|
13
|
+
|
14
|
+
def version
|
15
|
+
1
|
16
|
+
end
|
17
|
+
|
18
|
+
def code
|
19
|
+
@real_response.code.to_i
|
20
|
+
end
|
21
|
+
|
22
|
+
def plain_body
|
23
|
+
if @real_response.respond_to?(:plain_body)
|
24
|
+
@real_response.plain_body
|
25
|
+
else
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def headers
|
31
|
+
if @real_response.respond_to?(:headers)
|
32
|
+
@real_response.headers
|
33
|
+
else
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Quickbooks
|
2
|
+
module Service
|
3
|
+
module Responses
|
4
|
+
|
5
|
+
class OAuth2HttpResponse
|
6
|
+
include Quickbooks::Service::Responses::Methods
|
7
|
+
|
8
|
+
attr_accessor :real_response
|
9
|
+
|
10
|
+
# response : Faraday response
|
11
|
+
def initialize(response)
|
12
|
+
@real_response = response
|
13
|
+
end
|
14
|
+
|
15
|
+
def version
|
16
|
+
2
|
17
|
+
end
|
18
|
+
|
19
|
+
def body
|
20
|
+
@real_response.body
|
21
|
+
end
|
22
|
+
|
23
|
+
def plain_body
|
24
|
+
body
|
25
|
+
end
|
26
|
+
|
27
|
+
def code
|
28
|
+
@real_response.status.to_i
|
29
|
+
end
|
30
|
+
|
31
|
+
def headers
|
32
|
+
if @real_response.respond_to?(:headers)
|
33
|
+
@real_response.headers
|
34
|
+
else
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Quickbooks
|
2
|
+
module Service
|
3
|
+
module Responses
|
4
|
+
|
5
|
+
# This class just proxies and returns a wrapped response so that callers
|
6
|
+
# can invoke a common interface
|
7
|
+
class OAuthHttpResponse
|
8
|
+
|
9
|
+
def self.wrap(response)
|
10
|
+
if response.is_a?(OAuth2::Response)
|
11
|
+
Quickbooks::Service::Responses::OAuth2HttpResponse.new(response)
|
12
|
+
else
|
13
|
+
Quickbooks::Service::Responses::OAuth1HttpResponse.new(response)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -25,6 +25,10 @@ module Quickbooks
|
|
25
25
|
end until results.count < per_page
|
26
26
|
end
|
27
27
|
|
28
|
+
def exists?(field, selector, options={})
|
29
|
+
find_by(field, selector, options).count > 0
|
30
|
+
end
|
31
|
+
|
28
32
|
def find_by(field, selector, options={})
|
29
33
|
if field.class == Symbol
|
30
34
|
field = field.to_s.camelcase
|
@@ -9,7 +9,7 @@ module Quickbooks
|
|
9
9
|
# attachable: Quickbooks::Model::Attachable meta-data details, can be null
|
10
10
|
def upload(path_to_file, mime_type, attachable = nil)
|
11
11
|
url = url_for_resource("upload")
|
12
|
-
uploadIO =
|
12
|
+
uploadIO = class_for_io.new(path_to_file, mime_type)
|
13
13
|
response = do_http_file_upload(uploadIO, url, attachable)
|
14
14
|
prefix = "AttachableResponse/xmlns:Attachable"
|
15
15
|
if response.code.to_i == 200
|
@@ -19,6 +19,10 @@ module Quickbooks
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
+
def class_for_io
|
23
|
+
oauth.is_a?(OAuth2::AccessToken) ? Faraday::UploadIO : UploadIO
|
24
|
+
end
|
25
|
+
|
22
26
|
def download(uploadId)
|
23
27
|
url = url_for_resource("download/#{uploadId}")
|
24
28
|
do_http_get(url, {}, headers)
|
@@ -72,3 +72,14 @@ OAuth::AccessToken.class_eval do
|
|
72
72
|
request(:multipart_post, *args)
|
73
73
|
end
|
74
74
|
end
|
75
|
+
|
76
|
+
OAuth2::AccessToken.class_eval do
|
77
|
+
|
78
|
+
def post_with_multipart(*args)
|
79
|
+
multipart_post *args
|
80
|
+
end
|
81
|
+
|
82
|
+
def multipart_post(*args)
|
83
|
+
request(:post, *args)
|
84
|
+
end
|
85
|
+
end
|
data/lib/quickbooks/version.rb
CHANGED
metadata
CHANGED
@@ -1,49 +1,57 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: quickbooks-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cody Caughlan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-10-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: oauth
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.4.7
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.4.7
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: oauth2
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
16
30
|
requirements:
|
17
31
|
- - "~>"
|
18
32
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
33
|
+
version: '1.4'
|
20
34
|
type: :runtime
|
21
35
|
prerelease: false
|
22
36
|
version_requirements: !ruby/object:Gem::Requirement
|
23
37
|
requirements:
|
24
38
|
- - "~>"
|
25
39
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
40
|
+
version: '1.4'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: roxml
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
30
44
|
requirements:
|
31
|
-
- -
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: 3.3.1
|
34
|
-
- - "<"
|
45
|
+
- - '='
|
35
46
|
- !ruby/object:Gem::Version
|
36
|
-
version:
|
47
|
+
version: 4.0.0
|
37
48
|
type: :runtime
|
38
49
|
prerelease: false
|
39
50
|
version_requirements: !ruby/object:Gem::Requirement
|
40
51
|
requirements:
|
41
|
-
- -
|
42
|
-
- !ruby/object:Gem::Version
|
43
|
-
version: 3.3.1
|
44
|
-
- - "<"
|
52
|
+
- - '='
|
45
53
|
- !ruby/object:Gem::Version
|
46
|
-
version:
|
54
|
+
version: 4.0.0
|
47
55
|
- !ruby/object:Gem::Dependency
|
48
56
|
name: nokogiri
|
49
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -143,19 +151,33 @@ dependencies:
|
|
143
151
|
- !ruby/object:Gem::Version
|
144
152
|
version: 2.14.1
|
145
153
|
- !ruby/object:Gem::Dependency
|
146
|
-
name:
|
154
|
+
name: webmock
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: dotenv
|
147
169
|
requirement: !ruby/object:Gem::Requirement
|
148
170
|
requirements:
|
149
171
|
- - '='
|
150
172
|
- !ruby/object:Gem::Version
|
151
|
-
version:
|
173
|
+
version: 2.2.1
|
152
174
|
type: :development
|
153
175
|
prerelease: false
|
154
176
|
version_requirements: !ruby/object:Gem::Requirement
|
155
177
|
requirements:
|
156
178
|
- - '='
|
157
179
|
- !ruby/object:Gem::Version
|
158
|
-
version:
|
180
|
+
version: 2.2.1
|
159
181
|
description: QBO V3 REST API to Quickbooks Online
|
160
182
|
email: toolbag@gmail.com
|
161
183
|
executables: []
|
@@ -163,6 +185,7 @@ extensions: []
|
|
163
185
|
extra_rdoc_files: []
|
164
186
|
files:
|
165
187
|
- lib/quickbooks-ruby.rb
|
188
|
+
- lib/quickbooks/faraday/middleware/gzip.rb
|
166
189
|
- lib/quickbooks/model/access_token_response.rb
|
167
190
|
- lib/quickbooks/model/account.rb
|
168
191
|
- lib/quickbooks/model/account_based_expense_line_detail.rb
|
@@ -305,6 +328,10 @@ files:
|
|
305
328
|
- lib/quickbooks/service/refund_receipt.rb
|
306
329
|
- lib/quickbooks/service/refund_receipt_change.rb
|
307
330
|
- lib/quickbooks/service/reports.rb
|
331
|
+
- lib/quickbooks/service/responses/methods.rb
|
332
|
+
- lib/quickbooks/service/responses/oauth1_http_response.rb
|
333
|
+
- lib/quickbooks/service/responses/oauth2_http_response.rb
|
334
|
+
- lib/quickbooks/service/responses/oauth_http_response.rb
|
308
335
|
- lib/quickbooks/service/sales_receipt.rb
|
309
336
|
- lib/quickbooks/service/service_crud.rb
|
310
337
|
- lib/quickbooks/service/service_crud_json.rb
|