quickbooks-ruby 0.6.7 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|