quickbooks-ruby 0.4.0 → 1.0.16
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/lib/quickbooks-ruby.rb +65 -13
- data/lib/quickbooks/faraday/middleware/gzip.rb +74 -0
- data/lib/quickbooks/model/base_model.rb +13 -2
- data/lib/quickbooks/model/batch_request.rb +11 -6
- data/lib/quickbooks/model/batch_response.rb +11 -6
- data/lib/quickbooks/model/bill.rb +3 -0
- data/lib/quickbooks/model/change_data_capture.rb +50 -0
- data/lib/quickbooks/model/company_currency.rb +19 -0
- data/lib/quickbooks/model/company_info.rb +2 -1
- data/lib/quickbooks/model/custom_field.rb +18 -0
- data/lib/quickbooks/model/customer.rb +6 -1
- data/lib/quickbooks/model/customer_type.rb +15 -0
- data/lib/quickbooks/model/deposit_line_detail.rb +2 -2
- data/lib/quickbooks/model/description_line_detail.rb +7 -0
- data/lib/quickbooks/model/effective_tax_rate.rb +15 -0
- data/lib/quickbooks/model/exchange_rate.rb +23 -0
- data/lib/quickbooks/model/group_line_detail.rb +2 -1
- data/lib/quickbooks/model/invoice.rb +7 -1
- data/lib/quickbooks/model/invoice_change.rb +2 -1
- data/lib/quickbooks/model/invoice_group_line_detail.rb +14 -0
- data/lib/quickbooks/model/invoice_line_item.rb +34 -4
- data/lib/quickbooks/model/item.rb +11 -3
- data/lib/quickbooks/model/item_group_detail.rb +9 -0
- data/lib/quickbooks/model/item_group_line.rb +20 -0
- data/lib/quickbooks/model/line.rb +12 -0
- data/lib/quickbooks/model/line_ex.rb +9 -0
- data/lib/quickbooks/model/linked_transaction.rb +7 -0
- data/lib/quickbooks/model/physical_address.rb +2 -2
- data/lib/quickbooks/model/preferences.rb +28 -4
- data/lib/quickbooks/model/purchase_change.rb +7 -0
- data/lib/quickbooks/model/purchase_line_item.rb +1 -0
- data/lib/quickbooks/model/purchase_order.rb +3 -1
- data/lib/quickbooks/model/refund_receipt_change.rb +8 -0
- data/lib/quickbooks/model/report.rb +9 -7
- data/lib/quickbooks/model/sales_receipt.rb +4 -2
- data/lib/quickbooks/model/tax_rate.rb +1 -1
- data/lib/quickbooks/model/time_activity.rb +2 -1
- data/lib/quickbooks/model/transfer.rb +21 -0
- data/lib/quickbooks/model/vendor.rb +1 -0
- data/lib/quickbooks/service/access_token.rb +12 -11
- data/lib/quickbooks/service/base_service.rb +148 -24
- data/lib/quickbooks/service/batch.rb +1 -1
- data/lib/quickbooks/service/change_data_capture.rb +24 -0
- data/lib/quickbooks/service/company_currency.rb +18 -0
- data/lib/quickbooks/service/credit_memo.rb +6 -0
- data/lib/quickbooks/service/custom_field.rb +20 -0
- data/lib/quickbooks/service/customer.rb +5 -0
- data/lib/quickbooks/service/customer_type.rb +20 -0
- data/lib/quickbooks/service/estimate.rb +6 -0
- data/lib/quickbooks/service/exchange_rate.rb +25 -0
- data/lib/quickbooks/service/invoice.rb +24 -1
- data/lib/quickbooks/service/item.rb +5 -0
- data/lib/quickbooks/service/payment.rb +13 -0
- data/lib/quickbooks/service/preferences.rb +1 -1
- data/lib/quickbooks/service/purchase_change.rb +16 -0
- data/lib/quickbooks/service/purchase_order.rb +16 -0
- data/lib/quickbooks/service/refund_receipt_change.rb +16 -0
- data/lib/quickbooks/service/responses/methods.rb +17 -0
- data/lib/quickbooks/service/responses/oauth2_http_response.rb +43 -0
- data/lib/quickbooks/service/responses/oauth_http_response.rb +17 -0
- data/lib/quickbooks/service/sales_receipt.rb +30 -0
- data/lib/quickbooks/service/service_crud.rb +21 -0
- data/lib/quickbooks/service/service_crud_json.rb +1 -1
- data/lib/quickbooks/service/transfer.rb +16 -0
- data/lib/quickbooks/service/upload.rb +5 -1
- data/lib/quickbooks/util/logging.rb +4 -0
- data/lib/quickbooks/util/multipart.rb +2 -65
- data/lib/quickbooks/util/name_entity.rb +2 -1
- data/lib/quickbooks/util/query_builder.rb +6 -5
- data/lib/quickbooks/version.rb +1 -1
- metadata +73 -62
@@ -1,9 +1,16 @@
|
|
1
1
|
module Quickbooks
|
2
2
|
module Model
|
3
3
|
class LinkedTransaction < BaseModel
|
4
|
+
#== Constants
|
5
|
+
DEFAULT_TXN_TYPE = 'BillPaymentCheck'.freeze
|
6
|
+
|
4
7
|
xml_accessor :txn_id, :from => 'TxnId'
|
5
8
|
xml_accessor :txn_type, :from => 'TxnType'
|
6
9
|
xml_accessor :txn_line_id, :from => 'TxnLineId'
|
10
|
+
|
11
|
+
def bill_payment_check_type?
|
12
|
+
txn_type.to_s == DEFAULT_TXN_TYPE
|
13
|
+
end
|
7
14
|
end
|
8
15
|
end
|
9
16
|
end
|
@@ -19,16 +19,40 @@ module Quickbooks
|
|
19
19
|
PREFERENCE_SECTIONS = {
|
20
20
|
:accounting_info => %w(TrackDepartments DepartmentTerminology ClassTrackingPerTxnLine? ClassTrackingPerTxn? CustomerTerminology),
|
21
21
|
:product_and_services => %w(ForSales? ForPurchase? QuantityWithPriceAndRate? QuantityOnHand?),
|
22
|
-
:sales_forms => %w(CustomTxnNumbers? AllowDeposit? AllowDiscount? DefaultDiscountAccount? AllowEstimates? EstimateMessage?
|
23
|
-
ETransactionEnabledStatus? ETransactionAttachPDF? ETransactionPaymentEnabled? IPNSupportEnabled?
|
24
|
-
AllowServiceDate? AllowShipping? DefaultShippingAccount? DefaultTerms DefaultCustomerMessage),
|
25
22
|
:vendor_and_purchase => %w(TrackingByCustomer? BillableExpenseTracking? DefaultTerms? DefaultMarkup? POCustomField),
|
26
23
|
:time_tracking => %w(UseServices? BillCustomers? ShowBillRateToAll WorkWeekStartDate MarkTimeEntiresBillable?),
|
27
|
-
:tax => %w(UsingSalesTax?),
|
24
|
+
:tax => %w(UsingSalesTax? PartnerTaxEnabled?),
|
28
25
|
:currency => %w(MultiCurrencyEnabled? HomeCurrency),
|
29
26
|
:report => %w(ReportBasis)
|
30
27
|
}
|
31
28
|
|
29
|
+
xml_reader :sales_forms, :from => "SalesFormsPrefs", :as => create_preference_class(*%w(
|
30
|
+
AllowDeposit?
|
31
|
+
AllowDiscount?
|
32
|
+
AllowEstimates?
|
33
|
+
AllowServiceDate?
|
34
|
+
AllowShipping?
|
35
|
+
AutoApplyCredit?
|
36
|
+
CustomField?
|
37
|
+
CustomTxnNumbers?
|
38
|
+
DefaultCustomerMessage
|
39
|
+
DefaultDiscountAccount?
|
40
|
+
DefaultShippingAccount?
|
41
|
+
DefaultTerms
|
42
|
+
EmailCopyToCompany?
|
43
|
+
EstimateMessage
|
44
|
+
ETransactionAttachPDF?
|
45
|
+
ETransactionEnabledStatus
|
46
|
+
ETransactionPaymentEnabled?
|
47
|
+
IPNSupportEnabled?
|
48
|
+
SalesEmailBcc
|
49
|
+
SalesEmailCc
|
50
|
+
UsingPriceLevels?
|
51
|
+
UsingProgressInvoicing?
|
52
|
+
)) {
|
53
|
+
xml_reader :custom_fields, :as => [CustomField], :from => 'CustomField', in: 'CustomField'
|
54
|
+
}
|
55
|
+
|
32
56
|
PREFERENCE_SECTIONS.each do |section_name, fields|
|
33
57
|
xml_reader section_name, :from => "#{section_name}_prefs".camelize, :as => create_preference_class(*fields)
|
34
58
|
end
|
@@ -12,6 +12,7 @@ module Quickbooks
|
|
12
12
|
xml_accessor :description, :from => 'Description'
|
13
13
|
xml_accessor :amount, :from => 'Amount', :as => BigDecimal, :to_xml => Proc.new { |val| val.to_f }
|
14
14
|
xml_accessor :detail_type, :from => 'DetailType'
|
15
|
+
xml_accessor :received, :from => 'Received', :as => BigDecimal, :to_xml => to_xml_big_decimal
|
15
16
|
|
16
17
|
#== Various detail types
|
17
18
|
xml_accessor :account_based_expense_line_detail, :from => ACCOUNT_BASED_EXPENSE_LINE_DETAIL, :as => AccountBasedExpenseLineDetail
|
@@ -16,6 +16,7 @@ module Quickbooks
|
|
16
16
|
xml_accessor :txn_date, :from => 'TxnDate', :as => Date
|
17
17
|
xml_accessor :custom_fields, :from => 'CustomField', :as => [CustomField]
|
18
18
|
xml_accessor :private_note, :from => 'PrivateNote'
|
19
|
+
xml_accessor :memo, :from => 'Memo'
|
19
20
|
|
20
21
|
xml_accessor :linked_transactions, :from => 'LinkedTxn', :as => [LinkedTransaction]
|
21
22
|
xml_accessor :line_items, :from => 'Line', :as => [PurchaseLineItem]
|
@@ -23,10 +24,11 @@ module Quickbooks
|
|
23
24
|
xml_accessor :attachable_ref, :from => 'AttachableRef', :as => BaseReference
|
24
25
|
xml_accessor :vendor_ref, :from => 'VendorRef', :as => BaseReference
|
25
26
|
xml_accessor :ap_account_ref, :from => 'APAccountRef', :as => BaseReference
|
27
|
+
xml_accessor :po_email, from: "POEmail", as: EmailAddress
|
26
28
|
xml_accessor :class_ref, :from => 'ClassRef', :as => BaseReference
|
27
29
|
xml_accessor :sales_term_ref, :from => 'SalesTermRef', :as => BaseReference
|
28
30
|
|
29
|
-
xml_accessor :total, :from => 'TotalAmt', :as => BigDecimal, :to_xml =>
|
31
|
+
xml_accessor :total, :from => 'TotalAmt', :as => BigDecimal, :to_xml => to_xml_big_decimal
|
30
32
|
xml_accessor :due_date, :from => 'DueDate', :as => Date
|
31
33
|
xml_accessor :vendor_address, :from => 'VendorAddr', :as => PhysicalAddress
|
32
34
|
xml_accessor :ship_address, :from => 'ShipAddr', :as => PhysicalAddress
|
@@ -37,16 +37,18 @@ module Quickbooks
|
|
37
37
|
row_node.elements.map.with_index do |el, i|
|
38
38
|
value = el.attr('value')
|
39
39
|
|
40
|
-
if
|
41
|
-
|
42
|
-
|
43
|
-
nil
|
44
|
-
else
|
45
|
-
BigDecimal(value)
|
46
|
-
end
|
40
|
+
next nil if value.blank?
|
41
|
+
|
42
|
+
parse_row_value(value)
|
47
43
|
end
|
48
44
|
end
|
49
45
|
|
46
|
+
def parse_row_value(value)
|
47
|
+
BigDecimal(value)
|
48
|
+
rescue ArgumentError
|
49
|
+
value
|
50
|
+
end
|
51
|
+
|
50
52
|
end
|
51
53
|
end
|
52
54
|
end
|
@@ -20,6 +20,7 @@ module Quickbooks
|
|
20
20
|
xml_accessor :department_ref, :from => 'DepartmentRef', :as => BaseReference
|
21
21
|
xml_accessor :bill_email, :from => 'BillEmail', :as => EmailAddress
|
22
22
|
xml_accessor :bill_address, :from => 'BillAddr', :as => PhysicalAddress
|
23
|
+
xml_accessor :delivery_info, :from => 'DeliveryInfo', :as => DeliveryInfo
|
23
24
|
xml_accessor :ship_address, :from => 'ShipAddr', :as => PhysicalAddress
|
24
25
|
xml_accessor :po_number, :from => 'PONumber'
|
25
26
|
xml_accessor :ship_method_ref, :from => 'ShipMethodRef', :as => BaseReference
|
@@ -54,9 +55,10 @@ module Quickbooks
|
|
54
55
|
validate :line_item_size
|
55
56
|
validate :document_numbering
|
56
57
|
|
57
|
-
def
|
58
|
-
self.bill_email = EmailAddress.new(
|
58
|
+
def billing_email_address=(email_address_string)
|
59
|
+
self.bill_email = EmailAddress.new(email_address_string)
|
59
60
|
end
|
61
|
+
alias_method :email=, :billing_email_address= # backward backward compatibility to v0.4.6
|
60
62
|
|
61
63
|
end
|
62
64
|
end
|
@@ -16,7 +16,7 @@ module Quickbooks
|
|
16
16
|
xml_accessor :tax_return_line_ref, :from => "TaxReturnLineRef", :as => BaseReference
|
17
17
|
xml_accessor :special_tax_type, :from => "SpecialTaxType"
|
18
18
|
xml_accessor :display_type, :from => "DisplayType"
|
19
|
-
xml_accessor :effective_tax_rate, :from => "EffectiveTaxRate"
|
19
|
+
xml_accessor :effective_tax_rate, :from => "EffectiveTaxRate", :as => [EffectiveTaxRate]
|
20
20
|
|
21
21
|
validates_presence_of :name, :rate_value
|
22
22
|
|
@@ -43,10 +43,11 @@ 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)
|
49
|
-
errors.add(:employee_ref, "
|
50
|
+
errors.add(:employee_ref, "EmployeeRef is required and must be a non-zero value.")
|
50
51
|
end
|
51
52
|
end
|
52
53
|
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Quickbooks
|
2
|
+
module Model
|
3
|
+
class Transfer < BaseModel
|
4
|
+
XML_COLLECTION_NODE = "Transfer"
|
5
|
+
XML_NODE = "Transfer"
|
6
|
+
REST_RESOURCE = 'transfer'
|
7
|
+
|
8
|
+
xml_accessor :id, :from => 'Id'
|
9
|
+
xml_accessor :sync_token, :from => 'SyncToken', :as => Integer
|
10
|
+
xml_accessor :meta_data, :from => 'MetaData', :as => MetaData
|
11
|
+
xml_accessor :txn_date, :from => 'TxnDate', :as => Date
|
12
|
+
xml_accessor :currency_ref, :from => 'CurrencyRef', :as => BaseReference
|
13
|
+
xml_accessor :private_note, :from => 'PrivateNote'
|
14
|
+
xml_accessor :from_account_ref, :from => 'FromAccountRef', :as => BaseReference
|
15
|
+
xml_accessor :to_account_ref, :from => 'ToAccountRef', :as => BaseReference
|
16
|
+
xml_accessor :amount, :from => 'Amount', :as => BigDecimal, :to_xml => to_xml_big_decimal
|
17
|
+
|
18
|
+
reference_setters :currency_ref, :to_account_ref, :from_account_ref
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -41,6 +41,7 @@ module Quickbooks
|
|
41
41
|
xml_accessor :account_number, :from => 'AcctNum'
|
42
42
|
xml_accessor :is_1099?, :from => 'Vendor1099'
|
43
43
|
xml_accessor :currency_ref, :from => 'CurrencyRef', :as => BaseReference
|
44
|
+
xml_accessor :bill_rate, from: "BillRate", as: BigDecimal, to_xml: to_xml_big_decimal
|
44
45
|
|
45
46
|
#== Validations
|
46
47
|
validate :names_cannot_contain_invalid_characters
|
@@ -3,7 +3,7 @@ module Quickbooks
|
|
3
3
|
class AccessToken < BaseService
|
4
4
|
|
5
5
|
RENEW_URL = "https://appcenter.intuit.com/api/v1/connection/reconnect"
|
6
|
-
DISCONNECT_URL = "https://
|
6
|
+
DISCONNECT_URL = "https://developer.api.intuit.com/v2/oauth2/tokens/revoke"
|
7
7
|
|
8
8
|
# https://developer.intuit.com/docs/0025_quickbooksapi/0053_auth_auth/oauth_management_api#Reconnect
|
9
9
|
def renew
|
@@ -21,18 +21,19 @@ module Quickbooks
|
|
21
21
|
|
22
22
|
# https://developer.intuit.com/docs/0025_quickbooksapi/0053_auth_auth/oauth_management_api#Disconnect
|
23
23
|
def disconnect
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
if code == 200
|
29
|
-
result = Quickbooks::Model::AccessTokenResponse.from_xml(response.plain_body)
|
30
|
-
end
|
31
|
-
end
|
24
|
+
conn = Faraday.new
|
25
|
+
conn.basic_auth oauth.client.id, oauth.client.secret
|
26
|
+
url = "#{DISCONNECT_URL}?minorversion=#{Quickbooks.minorversion}"
|
27
|
+
response = conn.post(url, token: oauth.refresh_token || oauth.token)
|
32
28
|
|
33
|
-
|
29
|
+
if response.success?
|
30
|
+
Quickbooks::Model::AccessTokenResponse.new(error_code: "0")
|
31
|
+
else
|
32
|
+
Quickbooks::Model::AccessTokenResponse.new(
|
33
|
+
error_code: response.status.to_s, error_message: response.reason_phrase
|
34
|
+
)
|
35
|
+
end
|
34
36
|
end
|
35
|
-
|
36
37
|
end
|
37
38
|
end
|
38
39
|
end
|
@@ -7,8 +7,11 @@ module Quickbooks
|
|
7
7
|
attr_accessor :company_id
|
8
8
|
attr_accessor :oauth
|
9
9
|
attr_reader :base_uri
|
10
|
-
attr_reader :last_response_body
|
11
10
|
attr_reader :last_response_xml
|
11
|
+
attr_reader :last_response_intuit_tid
|
12
|
+
attr_accessor :before_request
|
13
|
+
attr_accessor :around_request
|
14
|
+
attr_accessor :after_request
|
12
15
|
|
13
16
|
XML_NS = %{xmlns="http://schema.intuit.com/finance/v3"}
|
14
17
|
HTTP_CONTENT_TYPE = 'application/xml'
|
@@ -17,6 +20,8 @@ module Quickbooks
|
|
17
20
|
BASE_DOMAIN = 'quickbooks.api.intuit.com'
|
18
21
|
SANDBOX_DOMAIN = 'sandbox-quickbooks.api.intuit.com'
|
19
22
|
|
23
|
+
RequestInfo = Struct.new(:url, :headers, :body, :method)
|
24
|
+
|
20
25
|
def initialize(attributes = {})
|
21
26
|
domain = Quickbooks.sandbox_mode ? SANDBOX_DOMAIN : BASE_DOMAIN
|
22
27
|
@base_uri = "https://#{domain}/v3/company"
|
@@ -25,6 +30,7 @@ module Quickbooks
|
|
25
30
|
|
26
31
|
def access_token=(token)
|
27
32
|
@oauth = token
|
33
|
+
rebuild_connection!
|
28
34
|
end
|
29
35
|
|
30
36
|
def company_id=(company_id)
|
@@ -36,6 +42,22 @@ module Quickbooks
|
|
36
42
|
@company_id = company_id
|
37
43
|
end
|
38
44
|
|
45
|
+
# def oauth_v2?
|
46
|
+
# @oauth.is_a? OAuth2::AccessToken
|
47
|
+
# end
|
48
|
+
|
49
|
+
# [OAuth2] The default Faraday connection does not have gzip or multipart support.
|
50
|
+
# We need to reset the existing connection and build a new one.
|
51
|
+
def rebuild_connection!
|
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
|
@@ -49,15 +71,19 @@ module Quickbooks
|
|
49
71
|
self.class::HTTP_CONTENT_TYPE == "application/json"
|
50
72
|
end
|
51
73
|
|
74
|
+
def is_pdf?
|
75
|
+
self.class::HTTP_CONTENT_TYPE == "application/pdf"
|
76
|
+
end
|
77
|
+
|
52
78
|
def default_model_query
|
53
79
|
"SELECT * FROM #{self.class.name.split("::").last}"
|
54
80
|
end
|
55
81
|
|
56
|
-
def url_for_query(query = nil, start_position = 1, max_results = 20)
|
82
|
+
def url_for_query(query = nil, start_position = 1, max_results = 20, options = {})
|
57
83
|
query ||= default_model_query
|
58
84
|
query = "#{query} STARTPOSITION #{start_position} MAXRESULTS #{max_results}"
|
59
85
|
|
60
|
-
"#{url_for_base}/query?query=#{
|
86
|
+
"#{url_for_base}/query?query=#{CGI.escape(query)}"
|
61
87
|
end
|
62
88
|
|
63
89
|
private
|
@@ -174,6 +200,21 @@ module Quickbooks
|
|
174
200
|
do_http(:get, url, {}, headers)
|
175
201
|
end
|
176
202
|
|
203
|
+
def do_http_raw_get(url, params = {}, headers = {})
|
204
|
+
url = add_query_string_to_url(url, params)
|
205
|
+
unless headers.has_key?('Content-Type')
|
206
|
+
headers['Content-Type'] = self.class::HTTP_CONTENT_TYPE
|
207
|
+
end
|
208
|
+
unless headers.has_key?('Accept')
|
209
|
+
headers['Accept'] = self.class::HTTP_ACCEPT
|
210
|
+
end
|
211
|
+
unless headers.has_key?('Accept-Encoding')
|
212
|
+
headers['Accept-Encoding'] = HTTP_ACCEPT_ENCODING
|
213
|
+
end
|
214
|
+
raw_response = oauth_get(url, headers)
|
215
|
+
Quickbooks::Service::Responses::OAuthHttpResponse.wrap(raw_response)
|
216
|
+
end
|
217
|
+
|
177
218
|
def do_http_file_upload(uploadIO, url, metadata = nil)
|
178
219
|
headers = {
|
179
220
|
'Content-Type' => 'multipart/form-data'
|
@@ -188,6 +229,8 @@ module Quickbooks
|
|
188
229
|
body['file_metadata_0'] = param_part
|
189
230
|
end
|
190
231
|
|
232
|
+
url = add_query_string_to_url(url, {})
|
233
|
+
|
191
234
|
do_http(:upload, url, body, headers)
|
192
235
|
end
|
193
236
|
|
@@ -211,31 +254,71 @@ module Quickbooks
|
|
211
254
|
log_request_body(body)
|
212
255
|
log "REQUEST HEADERS = #{headers.inspect}"
|
213
256
|
|
214
|
-
|
257
|
+
request_info = RequestInfo.new(url, headers, body, method)
|
258
|
+
before_request.call(request_info) if before_request
|
259
|
+
|
260
|
+
raw_response = with_around_request(request_info) do
|
261
|
+
case method
|
215
262
|
when :get
|
216
|
-
|
263
|
+
oauth_get(url, headers)
|
217
264
|
when :post
|
218
|
-
|
265
|
+
oauth_post(url, body, headers)
|
219
266
|
when :upload
|
220
|
-
|
267
|
+
oauth_post_with_multipart(url, body, headers)
|
221
268
|
else
|
222
269
|
raise "Do not know how to perform that HTTP operation"
|
223
270
|
end
|
224
|
-
|
271
|
+
end
|
272
|
+
|
273
|
+
after_request.call(request_info, raw_response.body) if after_request
|
274
|
+
|
275
|
+
response = Quickbooks::Service::Responses::OAuthHttpResponse.wrap(raw_response)
|
276
|
+
log "------ QUICKBOOKS-RUBY RESPONSE ------"
|
277
|
+
log "RESPONSE CODE = #{response.code}"
|
278
|
+
log_response_body(response)
|
279
|
+
if response.respond_to?(:headers)
|
280
|
+
log "RESPONSE HEADERS = #{response.headers}"
|
281
|
+
end
|
282
|
+
check_response(response, request: body)
|
283
|
+
end
|
284
|
+
|
285
|
+
def oauth_get(url, headers)
|
286
|
+
@oauth.get(url, headers: headers, raise_errors: false)
|
225
287
|
end
|
226
288
|
|
227
|
-
def
|
289
|
+
def oauth_post(url, body, headers)
|
290
|
+
@oauth.post(url, headers: headers, body: body, raise_errors: false)
|
291
|
+
end
|
292
|
+
|
293
|
+
def oauth_post_with_multipart(url, body, headers)
|
294
|
+
@oauth.post_with_multipart(url, headers: headers, body: body, raise_errors: false)
|
295
|
+
end
|
296
|
+
|
297
|
+
def add_query_string_to_url(url, params = {})
|
298
|
+
params ||= {}
|
299
|
+
params['minorversion'] = Quickbooks.minorversion
|
228
300
|
if params.is_a?(Hash) && !params.empty?
|
229
|
-
|
301
|
+
keyvalues = params.collect { |k| "#{k.first}=#{k.last}" }.join("&")
|
302
|
+
delim = url.index("?") != nil ? "&" : "?"
|
303
|
+
url + delim + keyvalues
|
230
304
|
else
|
231
305
|
url
|
232
306
|
end
|
233
307
|
end
|
234
308
|
|
235
309
|
def check_response(response, options = {})
|
236
|
-
|
237
|
-
|
238
|
-
|
310
|
+
if is_json?
|
311
|
+
parse_json(response.plain_body)
|
312
|
+
elsif !is_pdf?
|
313
|
+
parse_xml(response.plain_body)
|
314
|
+
end
|
315
|
+
|
316
|
+
@last_response_intuit_tid = if response.respond_to?(:headers) && response.headers
|
317
|
+
response.headers['intuit_tid']
|
318
|
+
else
|
319
|
+
nil
|
320
|
+
end
|
321
|
+
|
239
322
|
status = response.code.to_i
|
240
323
|
case status
|
241
324
|
when 200
|
@@ -248,12 +331,23 @@ module Quickbooks
|
|
248
331
|
when 302
|
249
332
|
raise "Unhandled HTTP Redirect"
|
250
333
|
when 401
|
251
|
-
raise Quickbooks::AuthorizationFailure
|
334
|
+
raise Quickbooks::AuthorizationFailure, parse_intuit_error
|
252
335
|
when 403
|
253
|
-
|
336
|
+
message = parse_intuit_error[:message]
|
337
|
+
if message.include?('ThrottleExceeded')
|
338
|
+
raise Quickbooks::ThrottleExceeded, message
|
339
|
+
end
|
340
|
+
raise Quickbooks::Forbidden, message
|
341
|
+
when 404
|
342
|
+
raise Quickbooks::NotFound
|
343
|
+
when 413
|
344
|
+
raise Quickbooks::RequestTooLarge
|
254
345
|
when 400, 500
|
255
346
|
parse_and_raise_exception(options)
|
256
|
-
when
|
347
|
+
when 429
|
348
|
+
message = parse_intuit_error[:message]
|
349
|
+
raise Quickbooks::TooManyRequests, message
|
350
|
+
when 502, 503, 504
|
257
351
|
raise Quickbooks::ServiceUnavailable
|
258
352
|
else
|
259
353
|
raise "HTTP Error Code: #{status}, Msg: #{response.plain_body}"
|
@@ -264,10 +358,10 @@ module Quickbooks
|
|
264
358
|
log "RESPONSE BODY:"
|
265
359
|
if is_json?
|
266
360
|
log ">>>>#{response.plain_body.inspect}"
|
267
|
-
|
361
|
+
elsif is_pdf?
|
362
|
+
log("BODY is a PDF : not dumping")
|
268
363
|
else
|
269
364
|
log(log_xml(response.plain_body))
|
270
|
-
parse_xml(response.plain_body)
|
271
365
|
end
|
272
366
|
end
|
273
367
|
|
@@ -275,8 +369,26 @@ module Quickbooks
|
|
275
369
|
log "REQUEST BODY:"
|
276
370
|
if is_json?
|
277
371
|
log(body.inspect)
|
372
|
+
elsif is_pdf?
|
373
|
+
log("BODY is a PDF : not dumping")
|
278
374
|
else
|
279
|
-
|
375
|
+
#multipart request for uploads arrive here in a Hash with UploadIO vals
|
376
|
+
if body.is_a?(Hash)
|
377
|
+
body.each do |k,v|
|
378
|
+
log('BODY PART:')
|
379
|
+
val_content = v.inspect
|
380
|
+
if v.is_a?(UploadIO)
|
381
|
+
if v.content_type == 'application/xml'
|
382
|
+
if v.io.is_a?(StringIO)
|
383
|
+
val_content = log_xml(v.io.string)
|
384
|
+
end
|
385
|
+
end
|
386
|
+
end
|
387
|
+
log("#{k}: #{val_content}")
|
388
|
+
end
|
389
|
+
else
|
390
|
+
log(log_xml(body))
|
391
|
+
end
|
280
392
|
end
|
281
393
|
end
|
282
394
|
|
@@ -291,17 +403,22 @@ module Quickbooks
|
|
291
403
|
else
|
292
404
|
ex.request_xml = options[:request]
|
293
405
|
end
|
406
|
+
ex.intuit_tid = err[:intuit_tid]
|
294
407
|
raise ex
|
295
408
|
end
|
296
409
|
|
297
410
|
def response_is_error?
|
298
|
-
|
299
|
-
|
300
|
-
|
411
|
+
begin
|
412
|
+
@last_response_xml.xpath("//xmlns:IntuitResponse/xmlns:Fault")[0] != nil
|
413
|
+
rescue Nokogiri::XML::XPath::SyntaxError => exception
|
414
|
+
#puts @last_response_xml.to_xml.to_s
|
415
|
+
#puts "WTF: #{exception.inspect}:#{exception.backtrace.join("\n")}"
|
416
|
+
true
|
417
|
+
end
|
301
418
|
end
|
302
419
|
|
303
420
|
def parse_intuit_error
|
304
|
-
error = {:message => "", :detail => "", :type => nil, :code => 0}
|
421
|
+
error = {:message => "", :detail => "", :type => nil, :code => 0, :intuit_tid => @last_response_intuit_tid}
|
305
422
|
fault = @last_response_xml.xpath("//xmlns:IntuitResponse/xmlns:Fault")[0]
|
306
423
|
if fault
|
307
424
|
error[:type] = fault.attributes['type'].value
|
@@ -313,7 +430,7 @@ module Quickbooks
|
|
313
430
|
error[:code] = code_attr.value
|
314
431
|
end
|
315
432
|
element_attr = error_element.attributes['element']
|
316
|
-
if
|
433
|
+
if element_attr
|
317
434
|
error[:element] = code_attr.value
|
318
435
|
end
|
319
436
|
error[:message] = error_element.xpath("//xmlns:Message").text
|
@@ -328,6 +445,13 @@ module Quickbooks
|
|
328
445
|
error
|
329
446
|
end
|
330
447
|
|
448
|
+
def with_around_request(request_info, &block)
|
449
|
+
if around_request
|
450
|
+
around_request.call(request_info, &block)
|
451
|
+
else
|
452
|
+
block.call
|
453
|
+
end
|
454
|
+
end
|
331
455
|
end
|
332
456
|
end
|
333
457
|
end
|