quickeebooks 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +3 -0
- data/Gemfile.lock +52 -0
- data/MIT-LICENSE +9 -0
- data/README.md +306 -0
- data/Rakefile +17 -0
- data/lib/quickeebooks.rb +89 -0
- data/lib/quickeebooks/online/model/account.rb +47 -0
- data/lib/quickeebooks/online/model/account_detail_type.rb +233 -0
- data/lib/quickeebooks/online/model/account_reference.rb +17 -0
- data/lib/quickeebooks/online/model/address.rb +42 -0
- data/lib/quickeebooks/online/model/customer.rb +66 -0
- data/lib/quickeebooks/online/model/customer_custom_field.rb +51 -0
- data/lib/quickeebooks/online/model/email.rb +24 -0
- data/lib/quickeebooks/online/model/intuit_type.rb +25 -0
- data/lib/quickeebooks/online/model/invoice.rb +50 -0
- data/lib/quickeebooks/online/model/invoice_header.rb +29 -0
- data/lib/quickeebooks/online/model/invoice_line_item.rb +22 -0
- data/lib/quickeebooks/online/model/item.rb +47 -0
- data/lib/quickeebooks/online/model/meta_data.rb +27 -0
- data/lib/quickeebooks/online/model/note.rb +11 -0
- data/lib/quickeebooks/online/model/open_balance.rb +11 -0
- data/lib/quickeebooks/online/model/phone.rb +12 -0
- data/lib/quickeebooks/online/model/price.rb +18 -0
- data/lib/quickeebooks/online/model/purchase_cost.rb +11 -0
- data/lib/quickeebooks/online/model/unit_price.rb +11 -0
- data/lib/quickeebooks/online/model/web_site.rb +16 -0
- data/lib/quickeebooks/online/service/account.rb +52 -0
- data/lib/quickeebooks/online/service/customer.rb +57 -0
- data/lib/quickeebooks/online/service/entitlement.rb +15 -0
- data/lib/quickeebooks/online/service/filter.rb +96 -0
- data/lib/quickeebooks/online/service/invoice.rb +50 -0
- data/lib/quickeebooks/online/service/item.rb +52 -0
- data/lib/quickeebooks/online/service/pagination.rb +19 -0
- data/lib/quickeebooks/online/service/service_base.rb +202 -0
- data/lib/quickeebooks/online/service/sort.rb +19 -0
- data/lib/quickeebooks/version.rb +5 -0
- data/lib/quickeebooks/windows/model/account.rb +67 -0
- data/lib/quickeebooks/windows/model/account_detail_type.rb +233 -0
- data/lib/quickeebooks/windows/model/account_reference.rb +19 -0
- data/lib/quickeebooks/windows/model/address.rb +36 -0
- data/lib/quickeebooks/windows/model/custom_field.rb +13 -0
- data/lib/quickeebooks/windows/model/customer.rb +109 -0
- data/lib/quickeebooks/windows/model/email.rb +44 -0
- data/lib/quickeebooks/windows/model/intuit_type.rb +17 -0
- data/lib/quickeebooks/windows/model/invoice.rb +44 -0
- data/lib/quickeebooks/windows/model/invoice_header.rb +65 -0
- data/lib/quickeebooks/windows/model/invoice_line_item.rb +38 -0
- data/lib/quickeebooks/windows/model/item.rb +84 -0
- data/lib/quickeebooks/windows/model/meta_data.rb +31 -0
- data/lib/quickeebooks/windows/model/note.rb +19 -0
- data/lib/quickeebooks/windows/model/open_balance.rb +11 -0
- data/lib/quickeebooks/windows/model/phone.rb +20 -0
- data/lib/quickeebooks/windows/model/price.rb +18 -0
- data/lib/quickeebooks/windows/model/purchase_cost.rb +12 -0
- data/lib/quickeebooks/windows/model/tax_line.rb +18 -0
- data/lib/quickeebooks/windows/model/unit_price.rb +12 -0
- data/lib/quickeebooks/windows/model/vendor_reference.rb +13 -0
- data/lib/quickeebooks/windows/model/web_site.rb +19 -0
- data/lib/quickeebooks/windows/service/account.rb +16 -0
- data/lib/quickeebooks/windows/service/customer.rb +16 -0
- data/lib/quickeebooks/windows/service/invoice.rb +27 -0
- data/lib/quickeebooks/windows/service/item.rb +18 -0
- data/lib/quickeebooks/windows/service/service_base.rb +176 -0
- data/quickeebooks.gemspec +27 -0
- data/spec/mocks/oauth_consumer_mock.rb +2 -0
- data/spec/quickeebooks/online/account_spec.rb +41 -0
- data/spec/quickeebooks/online/customer_spec.rb +46 -0
- data/spec/quickeebooks/online/invoice_spec.rb +15 -0
- data/spec/quickeebooks/online/services/account_spec.rb +84 -0
- data/spec/quickeebooks/online/services/customer_spec.rb +107 -0
- data/spec/quickeebooks/online/services/filter_spec.rb +43 -0
- data/spec/quickeebooks/online/services/service_base_spec.rb +30 -0
- data/spec/quickeebooks/online/services/sort_spec.rb +17 -0
- data/spec/quickeebooks/windows/customer_spec.rb +49 -0
- data/spec/quickeebooks_spec.rb +11 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/xml/online/account.xml +13 -0
- data/spec/xml/online/accounts.xml +108 -0
- data/spec/xml/online/customer.xml +63 -0
- data/spec/xml/online/customer2.xml +63 -0
- data/spec/xml/online/customers.xml +125 -0
- data/spec/xml/online/invoice.xml +33 -0
- data/spec/xml/online/user.xml +11 -0
- data/spec/xml/windows/customer.xml +56 -0
- data/spec/xml/windows/customers.xml +137 -0
- data/spec/xml/windows/http_401.xml +8 -0
- metadata +229 -0
@@ -0,0 +1,96 @@
|
|
1
|
+
module Quickeebooks
|
2
|
+
module Online
|
3
|
+
module Service
|
4
|
+
class Filter
|
5
|
+
|
6
|
+
DATE_FORMAT = '%Y-%m-%d'
|
7
|
+
DATE_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S%z'
|
8
|
+
|
9
|
+
attr_reader :type
|
10
|
+
attr_accessor :field, :value
|
11
|
+
|
12
|
+
# For Date/Time filtering
|
13
|
+
attr_accessor :before, :after
|
14
|
+
|
15
|
+
# For number comparisons
|
16
|
+
attr_accessor :gt, :lt, :eq
|
17
|
+
|
18
|
+
def initialize(type, *args)
|
19
|
+
@type = type
|
20
|
+
if args.first.is_a?(Hash)
|
21
|
+
args.first.each_pair do |key, value|
|
22
|
+
instance_variable_set("@#{key}", value)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
case @type.to_sym
|
29
|
+
when :date, :datetime
|
30
|
+
date_time_to_s
|
31
|
+
when :text
|
32
|
+
text_to_s
|
33
|
+
when :boolean
|
34
|
+
boolean_to_s
|
35
|
+
when :number
|
36
|
+
number_to_s
|
37
|
+
else
|
38
|
+
raise ArgumentError, "Don't know how to generate a Filter for type #{@type}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def number_to_s
|
45
|
+
clauses = []
|
46
|
+
if @eq
|
47
|
+
clauses << "#{@field} :EQUALS: #{@value}"
|
48
|
+
end
|
49
|
+
if @gt
|
50
|
+
clauses << "#{@field} :GreaterThan: #{@value}"
|
51
|
+
end
|
52
|
+
if @lt
|
53
|
+
clauses << "#{@field} :LessThan: #{@value}"
|
54
|
+
end
|
55
|
+
clauses.join(" :AND: ")
|
56
|
+
end
|
57
|
+
|
58
|
+
def date_time_to_s
|
59
|
+
clauses = []
|
60
|
+
if @before
|
61
|
+
raise ':before is not a valid DateTime/Time object' unless (@before.is_a?(Time) || @before.is_a?(DateTime))
|
62
|
+
clauses << "#{@field} :BEFORE: #{formatted_time(@before)}"
|
63
|
+
end
|
64
|
+
if @after
|
65
|
+
raise ':after is not a valid DateTime/Time object' unless (@after.is_a?(Time) || @after.is_a?(DateTime))
|
66
|
+
clauses << "#{@field} :AFTER: #{formatted_time(@after)}"
|
67
|
+
end
|
68
|
+
|
69
|
+
if @before.nil? && @after.nil?
|
70
|
+
clauses << "#{@field} :EQUALS: #{formatted_time(@value)}"
|
71
|
+
end
|
72
|
+
|
73
|
+
clauses.join(" :AND: ")
|
74
|
+
end
|
75
|
+
|
76
|
+
def text_to_s
|
77
|
+
"#{@field} :EQUALS: #{@value}"
|
78
|
+
end
|
79
|
+
|
80
|
+
def boolean_to_s
|
81
|
+
"#{@field} :EQUALS: #{@value}"
|
82
|
+
end
|
83
|
+
|
84
|
+
def formatted_time(time)
|
85
|
+
if time.is_a?(Date)
|
86
|
+
time.strftime(DATE_FORMAT)
|
87
|
+
elsif time.is_a?(DateTime) || time.is_a?(Time)
|
88
|
+
time.strftime(DATE_TIME_FORMAT)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'quickeebooks/online/service/service_base'
|
2
|
+
require 'quickeebooks/online/model/invoice'
|
3
|
+
require 'quickeebooks/online/model/invoice_header'
|
4
|
+
require 'quickeebooks/online/model/invoice_line_item'
|
5
|
+
require 'tempfile'
|
6
|
+
|
7
|
+
module Quickeebooks
|
8
|
+
module Online
|
9
|
+
module Service
|
10
|
+
class Invoice < ServiceBase
|
11
|
+
|
12
|
+
|
13
|
+
# Fetch a +Collection+ of +Invoice+ objects
|
14
|
+
# Arguments:
|
15
|
+
# filters: Array of +Filter+ objects to apply
|
16
|
+
# page: +Fixnum+ Starting page
|
17
|
+
# per_page: +Fixnum+ How many results to fetch per page
|
18
|
+
# sort: +Sort+ object
|
19
|
+
# options: +Hash+ extra arguments
|
20
|
+
def list(filters = [], page = 1, per_page = 20, sort = nil, options = {})
|
21
|
+
fetch_collection("invoices", "Invoice", Quickeebooks::Online::Model::Invoice, filters, page, per_page, sort, options)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns the absolute path to the PDF on disk
|
25
|
+
# Its left to the caller to unlink the file at some later date
|
26
|
+
# Returns: +String+ : absolute path to file on disk or nil if couldnt fetch PDF
|
27
|
+
def invoice_as_pdf(invoice_id, destination_parent_directory)
|
28
|
+
response = do_http_get("#{url_for_resource("invoice-document")}/#{invoice_id}", {}, {'Content-Type' => 'application/pdf'})
|
29
|
+
if response && response.code.to_i == 200
|
30
|
+
file_name = File.join(destination_parent_directory, "invoice-document-#{invoice_id}-#{Time.now.strftime('%Y-%m-%d_%H-%M')}.pdf")
|
31
|
+
File.open(file_name, "wb") do |file|
|
32
|
+
file.write(response.body)
|
33
|
+
end
|
34
|
+
file_name
|
35
|
+
else
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Fetch an invoice by its ID
|
41
|
+
# Returns: +Invoice+ object
|
42
|
+
def fetch_by_id(invoice_id)
|
43
|
+
response = do_http_get("#{url_for_resource("invoice")}/#{invoice_id}")
|
44
|
+
Quickeebooks::Online::Model::Invoice.from_xml(response.body)
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'quickeebooks/online/model/item'
|
2
|
+
require 'quickeebooks/online/service/service_base'
|
3
|
+
|
4
|
+
module Quickeebooks
|
5
|
+
module Online
|
6
|
+
module Service
|
7
|
+
class Item < ServiceBase
|
8
|
+
|
9
|
+
def list(filters = [], page = 1, per_page = 20, sort = nil, options = {})
|
10
|
+
fetch_collection("items", "Item", Quickeebooks::Online::Model::Item, filters, page, per_page, sort, options)
|
11
|
+
end
|
12
|
+
|
13
|
+
def create(item)
|
14
|
+
raise InvalidModelException unless item.valid?
|
15
|
+
xml = item.to_xml_ns
|
16
|
+
response = do_http_post(url_for_resource("item"), valid_xml_document(xml))
|
17
|
+
if response && response.code.to_i == 200
|
18
|
+
Quickeebooks::Online::Model::Item.from_xml(response.body)
|
19
|
+
else
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def update(item)
|
25
|
+
raise InvalidModelException unless item.valid?
|
26
|
+
xml = item.to_xml_ns
|
27
|
+
url = "#{url_for_resource("item")}/#{item.id}"
|
28
|
+
response = do_http_post(url, valid_xml_document(xml))
|
29
|
+
if response && response.code.to_i == 200
|
30
|
+
Quickeebooks::Online::Model::Item.from_xml(response.body)
|
31
|
+
else
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def fetch_by_id(id)
|
37
|
+
response = do_http_get("#{url_for_resource("item")}/#{id}")
|
38
|
+
Quickeebooks::Online::Model::Item.from_xml(response.body)
|
39
|
+
end
|
40
|
+
|
41
|
+
def delete(item)
|
42
|
+
raise InvalidModelException.new("Missing required parameters for delete") unless item.valid_for_deletion?
|
43
|
+
xml = valid_xml_document(item.to_xml_ns(:fields => ['Id', 'SyncToken']))
|
44
|
+
url = "#{url_for_resource("item")}/#{item.id}"
|
45
|
+
response = do_http_post(url, xml, {:methodx => "delete"})
|
46
|
+
response.code.to_i == 200
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Quickeebooks
|
2
|
+
module Online
|
3
|
+
module Service
|
4
|
+
class Pagination
|
5
|
+
attr_accessor :page, :results_per_page
|
6
|
+
|
7
|
+
def initialize(page, results_per_page)
|
8
|
+
@page = page
|
9
|
+
@results_per_page = results_per_page
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
"PageNum=#{@page}\nResultsPerPage=#{@results_per_page}"
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,202 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
require 'uri'
|
3
|
+
require 'cgi'
|
4
|
+
|
5
|
+
class IntuitRequestException < Exception
|
6
|
+
attr_accessor :code, :cause
|
7
|
+
def initialize(msg)
|
8
|
+
super(msg)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
class AuthorizationFailure < Exception; end
|
12
|
+
|
13
|
+
module Quickeebooks
|
14
|
+
module Online
|
15
|
+
|
16
|
+
module Service
|
17
|
+
class ServiceBase
|
18
|
+
attr_accessor :realm_id
|
19
|
+
attr_accessor :oauth
|
20
|
+
attr_reader :base_uri
|
21
|
+
|
22
|
+
QB_BASE_URI = "https://qbo.intuit.com/qbo1/rest/user/v2"
|
23
|
+
XML_NS = %{xmlns:ns2="http://www.intuit.com/sb/cdm/qbo" xmlns="http://www.intuit.com/sb/cdm/v2" xmlns:ns3="http://www.intuit.com/sb/cdm"}
|
24
|
+
|
25
|
+
def initialize(oauth_consumer_token, realm_id, base_url = nil)
|
26
|
+
@oauth = oauth_consumer_token
|
27
|
+
@realm_id = realm_id
|
28
|
+
if base_url.nil?
|
29
|
+
determine_base_url
|
30
|
+
else
|
31
|
+
uri = URI.parse(base_url)
|
32
|
+
if uri.host.nil?
|
33
|
+
raise ArgumentError, "#{base_url} doesn't appear to be a valid host name!"
|
34
|
+
end
|
35
|
+
@base_uri = base_url
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Given a realm ID we need to determine the real Base URL
|
40
|
+
# to use for all subsequenet REST operations
|
41
|
+
# See: https://ipp.developer.intuit.com/0010_Intuit_Partner_Platform/0050_Data_Services/0400_QuickBooks_Online/0100_Calling_Data_Services/0010_Getting_the_Base_URL
|
42
|
+
def determine_base_url
|
43
|
+
response = @oauth.request(:get, qb_base_uri_with_realm_id)
|
44
|
+
if response
|
45
|
+
if response.code == "200"
|
46
|
+
doc = parse_xml(response.body)
|
47
|
+
element = doc.xpath("//qbo:QboUser/qbo:CurrentCompany/qbo:BaseURI")[0]
|
48
|
+
if element
|
49
|
+
@base_uri = element.text
|
50
|
+
end
|
51
|
+
else
|
52
|
+
raise IntuitRequestException.new("Response error: invalid code #{response.code}")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def url_for_resource(resource)
|
58
|
+
url_for_base("resource/#{resource}")
|
59
|
+
#{}"#{@base_uri}/resource/#{resource}/v2/#{@realm_id}"
|
60
|
+
end
|
61
|
+
|
62
|
+
def url_for_base(raw)
|
63
|
+
"#{@base_uri}/#{raw}/v2/#{@realm_id}"
|
64
|
+
end
|
65
|
+
|
66
|
+
def qb_base_uri_with_realm_id
|
67
|
+
"#{QB_BASE_URI}/#{@realm_id}"
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def parse_xml(xml)
|
73
|
+
Nokogiri::XML(xml)
|
74
|
+
end
|
75
|
+
|
76
|
+
def valid_xml_document(xml)
|
77
|
+
%Q{<?xml version="1.0" encoding="utf-8"?>\n#{xml.strip}}
|
78
|
+
end
|
79
|
+
|
80
|
+
def fetch_collection(resource, container, model, filters = [], page = 1, per_page = 20, sort = nil, options ={})
|
81
|
+
raise ArgumentError, "missing resource to fetch" if resource.nil?
|
82
|
+
raise ArgumentError, "missing result container" if container.nil?
|
83
|
+
raise ArgumentError, "missing model to instantiate" if model.nil?
|
84
|
+
|
85
|
+
post_body_lines = []
|
86
|
+
|
87
|
+
if filters.is_a?(Array) && filters.length > 0
|
88
|
+
filter_string = filters.collect { |f| f.to_s }
|
89
|
+
post_body_lines << "Filter=#{CGI.escape(filter_string.join(" :AND: "))}"
|
90
|
+
end
|
91
|
+
|
92
|
+
post_body_lines << "PageNum=#{page}"
|
93
|
+
post_body_lines << "ResultsPerPage=#{per_page}"
|
94
|
+
|
95
|
+
if sort
|
96
|
+
post_body_lines << "Sort=#{CGI.escape(sort.to_s)}"
|
97
|
+
end
|
98
|
+
|
99
|
+
body = post_body_lines.join("&")
|
100
|
+
response = do_http_post(url_for_resource(resource), body, {}, {'Content-Type' => 'application/x-www-form-urlencoded'})
|
101
|
+
if response
|
102
|
+
collection = Quickeebooks::Collection.new
|
103
|
+
xml = parse_xml(response.body)
|
104
|
+
begin
|
105
|
+
results = []
|
106
|
+
collection.count = xml.xpath("//qbo:SearchResults/qbo:Count")[0].text.to_i
|
107
|
+
if collection.count > 0
|
108
|
+
xml.xpath("//qbo:SearchResults/qbo:CdmCollections/xmlns:#{container}").each do |xa|
|
109
|
+
results << model.from_xml(xa)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
collection.entries = results
|
113
|
+
collection.current_page = xml.xpath("//qbo:SearchResults/qbo:CurrentPage")[0].text.to_i
|
114
|
+
rescue => ex
|
115
|
+
log("Error parsing XML: #{ex.message}")
|
116
|
+
raise IntuitRequestException.new("Error parsing XML: #{ex.message}")
|
117
|
+
end
|
118
|
+
collection
|
119
|
+
else
|
120
|
+
nil
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def do_http_post(url, body = "", params = {}, headers = {}) # throws IntuitRequestException
|
125
|
+
url = add_query_string_to_url(url, params)
|
126
|
+
do_http(:post, url, body, headers)
|
127
|
+
end
|
128
|
+
|
129
|
+
def do_http_get(url, params = {}, headers = {}) # throws IntuitRequestException
|
130
|
+
url = add_query_string_to_url(url, params)
|
131
|
+
do_http(:get, url, "", headers)
|
132
|
+
end
|
133
|
+
|
134
|
+
def do_http(method, url, body, headers) # throws IntuitRequestException
|
135
|
+
unless headers.has_key?('Content-Type')
|
136
|
+
headers.merge!({'Content-Type' => 'application/xml'})
|
137
|
+
end
|
138
|
+
# puts "METHOD = #{method}"
|
139
|
+
# puts "URL = #{url}"
|
140
|
+
# puts "BODY = #{body == nil ? "<NIL>" : body}"
|
141
|
+
# puts "HEADERS = #{headers.inspect}"
|
142
|
+
response = @oauth.request(method, url, body, headers)
|
143
|
+
check_response(response)
|
144
|
+
end
|
145
|
+
|
146
|
+
def add_query_string_to_url(url, params)
|
147
|
+
if params.is_a?(Hash) && !params.empty?
|
148
|
+
url + "?" + params.collect { |k| "#{k.first}=#{k.last}" }.join("&")
|
149
|
+
else
|
150
|
+
url
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def check_response(response)
|
155
|
+
#puts "HTTP Response: #{response.code}"
|
156
|
+
status = response.code.to_i
|
157
|
+
case status
|
158
|
+
when 200
|
159
|
+
response
|
160
|
+
when 302
|
161
|
+
raise "Unhandled HTTP Redirect"
|
162
|
+
when 401
|
163
|
+
raise AuthorizationFailure
|
164
|
+
when 400, 500
|
165
|
+
err = parse_intuit_error(response.body)
|
166
|
+
ex = IntuitRequestException.new(err[:message])
|
167
|
+
ex.code = err[:code]
|
168
|
+
ex.cause = err[:cause]
|
169
|
+
raise ex
|
170
|
+
else
|
171
|
+
raise "HTTP Error Code: #{status}, Msg: #{response.body}"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def parse_intuit_error(body)
|
176
|
+
xml = parse_xml(body)
|
177
|
+
error = {:message => "", :code => 0, :cause => ""}
|
178
|
+
fault = xml.xpath("//xmlns:FaultInfo/xmlns:Message")[0]
|
179
|
+
if fault
|
180
|
+
error[:message] = fault.text
|
181
|
+
end
|
182
|
+
error_code = xml.xpath("//xmlns:FaultInfo/xmlns:ErrorCode")[0]
|
183
|
+
if error_code
|
184
|
+
error[:code] = error_code.text
|
185
|
+
end
|
186
|
+
error_cause = xml.xpath("//xmlns:FaultInfo/xmlns:Cause")[0]
|
187
|
+
if error_cause
|
188
|
+
error[:cause] = error_cause.text
|
189
|
+
end
|
190
|
+
|
191
|
+
error
|
192
|
+
end
|
193
|
+
|
194
|
+
def log(msg)
|
195
|
+
Quickeebooks.logger.info(msg)
|
196
|
+
Quickeebooks.logger.flush if Quickeebooks.logger.respond_to?(:flush)
|
197
|
+
end
|
198
|
+
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|