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,19 @@
|
|
1
|
+
require "quickeebooks"
|
2
|
+
|
3
|
+
module Quickeebooks
|
4
|
+
module Windows
|
5
|
+
module Model
|
6
|
+
class WebSite < Quickeebooks::Windows::Model::IntuitType
|
7
|
+
xml_accessor :id, :from => 'Id'
|
8
|
+
xml_accessor :uri, :from => 'URI'
|
9
|
+
xml_accessor :tag, :from => 'Tag'
|
10
|
+
xml_accessor :default, :from => 'Default'
|
11
|
+
|
12
|
+
def to_xml(options = {})
|
13
|
+
return "" if uri.to_s.empty?
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'quickeebooks/windows/model/account'
|
2
|
+
require 'quickeebooks/windows/service/service_base'
|
3
|
+
|
4
|
+
module Quickeebooks
|
5
|
+
module Windows
|
6
|
+
module Service
|
7
|
+
class Account < Quickeebooks::Windows::Service::ServiceBase
|
8
|
+
|
9
|
+
def list(filters = [], page = 1, per_page = 20, sort = nil, options = {})
|
10
|
+
fetch_collection("accounts", "Account", Quickeebooks::Windows::Model::Account, nil, filters, page, per_page, sort, options)
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'quickeebooks/windows/service/service_base'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
module Quickeebooks
|
5
|
+
module Windows
|
6
|
+
module Service
|
7
|
+
class Customer < Quickeebooks::Windows::Service::ServiceBase
|
8
|
+
|
9
|
+
def list(filters = [], page = 1, per_page = 20, sort = nil, options = {})
|
10
|
+
fetch_collection("customer", "Customer", Quickeebooks::Windows::Model::Customer, nil, filters, page, per_page, sort, options)
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'quickeebooks/windows/service/service_base'
|
2
|
+
require 'quickeebooks/windows/model/invoice'
|
3
|
+
require 'quickeebooks/windows/model/invoice_header'
|
4
|
+
require 'quickeebooks/windows/model/invoice_line_item'
|
5
|
+
require 'tempfile'
|
6
|
+
|
7
|
+
module Quickeebooks
|
8
|
+
module Windows
|
9
|
+
module Service
|
10
|
+
class Invoice < Quickeebooks::Windows::Service::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::Windows::Model::Invoice, nil, filters, page, per_page, sort, options)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'quickeebooks/windows/model/item'
|
2
|
+
require 'quickeebooks/windows/service/service_base'
|
3
|
+
|
4
|
+
module Quickeebooks
|
5
|
+
module Windows
|
6
|
+
module Service
|
7
|
+
class Item < Quickeebooks::Windows::Service::ServiceBase
|
8
|
+
|
9
|
+
def list(filters = [], page = 1, per_page = 20, sort = nil, options = {})
|
10
|
+
custom_field_query = '<ItemQuery xmlns="http://www.intuit.com/sb/cdm/v2"><CustomFieldEnable>true</CustomFieldEnable></ItemQuery>'
|
11
|
+
fetch_collection("item", "Item", Quickeebooks::Windows::Model::Item, custom_field_query, filters, page, per_page, sort, options)
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,176 @@
|
|
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 Windows
|
15
|
+
module Service
|
16
|
+
class ServiceBase
|
17
|
+
|
18
|
+
attr_accessor :realm_id
|
19
|
+
attr_accessor :oauth
|
20
|
+
attr_reader :base_uri
|
21
|
+
attr_reader :last_response_body
|
22
|
+
attr_reader :last_response_xml
|
23
|
+
|
24
|
+
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"}
|
25
|
+
|
26
|
+
def initialize(oauth_access_token, realm_id)
|
27
|
+
@base_uri = 'https://services.intuit.com/sb'
|
28
|
+
@oauth = oauth_access_token
|
29
|
+
@realm_id = realm_id
|
30
|
+
end
|
31
|
+
|
32
|
+
def url_for_resource(resource)
|
33
|
+
url_for_base(resource)
|
34
|
+
end
|
35
|
+
|
36
|
+
def url_for_base(raw)
|
37
|
+
"#{@base_uri}/#{raw}/v2/#{@realm_id}"
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def parse_xml(xml)
|
43
|
+
@last_response_xml ||=
|
44
|
+
begin
|
45
|
+
x = Nokogiri::XML(xml)
|
46
|
+
#x.document.remove_namespaces!
|
47
|
+
x
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def valid_xml_document(xml)
|
52
|
+
%Q{<?xml version="1.0" encoding="utf-8"?>\n#{xml.strip}}
|
53
|
+
end
|
54
|
+
|
55
|
+
def fetch_collection(resource, container, model, custom_field_query = nil, filters = [], page = 1, per_page = 20, sort = nil, options ={})
|
56
|
+
raise ArgumentError, "missing resource to fetch" if resource.nil?
|
57
|
+
raise ArgumentError, "missing result container" if container.nil?
|
58
|
+
raise ArgumentError, "missing model to instantiate" if model.nil?
|
59
|
+
|
60
|
+
if custom_field_query != nil
|
61
|
+
response = do_http_post(url_for_resource(resource), custom_field_query, {}, {'Content-Type' => 'text/xml'})
|
62
|
+
else
|
63
|
+
response = do_http_get(url_for_resource(resource), {}, {'Content-Type' => 'text/html'})
|
64
|
+
end
|
65
|
+
if response
|
66
|
+
collection = Quickeebooks::Collection.new
|
67
|
+
xml = parse_xml(response.body)
|
68
|
+
begin
|
69
|
+
results = []
|
70
|
+
path_to_nodes = "//xmlns:RestResponse/xmlns:#{model::XML_COLLECTION_NODE}/xmlns:#{model::XML_NODE}"
|
71
|
+
collection.count = xml.xpath(path_to_nodes).count
|
72
|
+
if collection.count > 0
|
73
|
+
xml.xpath(path_to_nodes).each do |xa|
|
74
|
+
entry = model.from_xml(xa)
|
75
|
+
results << entry
|
76
|
+
end
|
77
|
+
end
|
78
|
+
collection.entries = results
|
79
|
+
collection.current_page = 1 # TODO: fix this
|
80
|
+
rescue => ex
|
81
|
+
log("Error parsing XML: #{ex.message}")
|
82
|
+
raise IntuitRequestException.new("Error parsing XML: #{ex.message}")
|
83
|
+
end
|
84
|
+
collection
|
85
|
+
else
|
86
|
+
nil
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def do_http_post(url, body = "", params = {}, headers = {}) # throws IntuitRequestException
|
91
|
+
url = add_query_string_to_url(url, params)
|
92
|
+
do_http(:post, url, body, headers)
|
93
|
+
end
|
94
|
+
|
95
|
+
def do_http_get(url, params = {}, headers = {}) # throws IntuitRequestException
|
96
|
+
url = add_query_string_to_url(url, params)
|
97
|
+
do_http(:get, url, "", headers)
|
98
|
+
end
|
99
|
+
|
100
|
+
def do_http(method, url, body, headers) # throws IntuitRequestException
|
101
|
+
unless headers.has_key?('Content-Type')
|
102
|
+
headers.merge!({'Content-Type' => 'application/xml'})
|
103
|
+
end
|
104
|
+
# puts "METHOD = #{method}"
|
105
|
+
# puts "URL = #{url}"
|
106
|
+
# puts "BODY = #{body == nil ? "<NIL>" : body}"
|
107
|
+
# puts "HEADERS = #{headers.inspect}"
|
108
|
+
response = @oauth.request(method, url, body, headers)
|
109
|
+
check_response(response)
|
110
|
+
end
|
111
|
+
|
112
|
+
def add_query_string_to_url(url, params)
|
113
|
+
if params.is_a?(Hash) && !params.empty?
|
114
|
+
url + "?" + params.collect { |k| "#{k.first}=#{k.last}" }.join("&")
|
115
|
+
else
|
116
|
+
url
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def check_response(response)
|
121
|
+
#puts "HTTP Response: #{response.code}"
|
122
|
+
parse_xml(response.body)
|
123
|
+
status = response.code.to_i
|
124
|
+
case status
|
125
|
+
when 200
|
126
|
+
# even HTTP 200 can contain an error, so we always have to peek for an Error
|
127
|
+
if response_is_error?
|
128
|
+
parse_and_raise_exceptione
|
129
|
+
else
|
130
|
+
response
|
131
|
+
end
|
132
|
+
when 302
|
133
|
+
raise "Unhandled HTTP Redirect"
|
134
|
+
when 401
|
135
|
+
raise AuthorizationFailure
|
136
|
+
when 400, 500
|
137
|
+
parse_and_raise_exceptione
|
138
|
+
else
|
139
|
+
raise "HTTP Error Code: #{status}, Msg: #{response.body}"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def parse_and_raise_exceptione
|
144
|
+
err = parse_intuit_error
|
145
|
+
ex = IntuitRequestException.new(err[:message])
|
146
|
+
ex.code = err[:code]
|
147
|
+
ex.cause = err[:cause]
|
148
|
+
raise ex
|
149
|
+
end
|
150
|
+
|
151
|
+
def response_is_error?
|
152
|
+
@last_response_xml.xpath("//xmlns:RestResponse/xmlns:Error").first != nil
|
153
|
+
end
|
154
|
+
|
155
|
+
def parse_intuit_error
|
156
|
+
error = {:message => "", :code => 0, :cause => ""}
|
157
|
+
fault = @last_response_xml.xpath("//xmlns:RestResponse/xmlns:Error/xmlns:ErrorDesc")[0]
|
158
|
+
if fault
|
159
|
+
error[:message] = fault.text
|
160
|
+
end
|
161
|
+
error_code = @last_response_xml.xpath("//xmlns:RestResponse/xmlns:Error/xmlns:ErrorCode")[0]
|
162
|
+
if error_code
|
163
|
+
error[:code] = error_code.text
|
164
|
+
end
|
165
|
+
error
|
166
|
+
end
|
167
|
+
|
168
|
+
def log(msg)
|
169
|
+
Quickeebooks.logger.info(msg)
|
170
|
+
Quickeebooks.logger.flush if Quickeebooks.logger.respond_to?(:flush)
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
$:.unshift File.expand_path("../lib", __FILE__)
|
2
|
+
require "quickeebooks/version"
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.name = "quickeebooks"
|
6
|
+
gem.version = Quickeebooks::VERSION
|
7
|
+
|
8
|
+
gem.author = "Cody Caughlan"
|
9
|
+
gem.email = "toolbag@gmail.com"
|
10
|
+
gem.homepage = "http://github.com/ruckus/quickeebooks"
|
11
|
+
gem.summary = "REST integration with Quickbooks Online"
|
12
|
+
|
13
|
+
gem.description = gem.summary
|
14
|
+
|
15
|
+
gem.files = Dir["**/*"]#.select { |d| d =~ %r{^(README|bin/|data/|ext/|lib/|spec/|test/)} }
|
16
|
+
|
17
|
+
gem.add_dependency 'roxml'
|
18
|
+
gem.add_dependency 'oauth'
|
19
|
+
gem.add_dependency 'nokogiri'
|
20
|
+
gem.add_dependency 'activemodel'
|
21
|
+
|
22
|
+
gem.add_development_dependency 'rake'
|
23
|
+
gem.add_development_dependency 'rcov', '~> 0.9.8'
|
24
|
+
gem.add_development_dependency 'rr', '~> 1.0.2'
|
25
|
+
gem.add_development_dependency 'rspec', '~> 2.0.0'
|
26
|
+
gem.add_development_dependency 'fakeweb'
|
27
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "stringio"
|
3
|
+
require "quickeebooks/online/model/account"
|
4
|
+
|
5
|
+
describe "Quickeebooks::Online::Model::Account" do
|
6
|
+
|
7
|
+
it "should convert Ruby to XML" do
|
8
|
+
account = Quickeebooks::Online::Model::Account.new
|
9
|
+
account.sync_token = 1
|
10
|
+
account.name = "John Doe"
|
11
|
+
expected = "<Account><Name>John Doe</Name></Account>"
|
12
|
+
# we have to jump through some hoops to get a non-pretty version for easier comparison
|
13
|
+
s = StringIO.new
|
14
|
+
xml = account.to_xml(:fields => 'Name').write_to(s, :indent => 0, :indent_text => '')
|
15
|
+
xml.string.gsub(/\n/, '').should == expected
|
16
|
+
end
|
17
|
+
|
18
|
+
it "has a validation error on invalid subtype" do
|
19
|
+
account = Quickeebooks::Online::Model::Account.new
|
20
|
+
account.sub_type = "invalid"
|
21
|
+
account.valid?.should == false
|
22
|
+
end
|
23
|
+
|
24
|
+
it "has a validation error when required attributes are not given" do
|
25
|
+
account = Quickeebooks::Online::Model::Account.new
|
26
|
+
account.valid?.should == false
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should pass validation when all required attributes are given" do
|
30
|
+
account = Quickeebooks::Online::Model::Account.new
|
31
|
+
account.name = "John Doe"
|
32
|
+
account.sub_type = "AccountsPayable"
|
33
|
+
account.valid?.should == true
|
34
|
+
end
|
35
|
+
|
36
|
+
it "cannot delete an Account without providing required fields" do
|
37
|
+
account = Quickeebooks::Online::Model::Account.new
|
38
|
+
account.valid_for_deletion?.should == false
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
require "quickeebooks/online/model/customer"
|
4
|
+
|
5
|
+
describe "Quickeebooks::Online::Model::Customer" do
|
6
|
+
|
7
|
+
it "parse customer from XML" do
|
8
|
+
xml = File.read(File.dirname(__FILE__) + "/../../xml/online/customer.xml")
|
9
|
+
customer = Quickeebooks::Online::Model::Customer.from_xml(xml)
|
10
|
+
customer.sync_token.should == 1
|
11
|
+
customer.name.should == "John Doe"
|
12
|
+
|
13
|
+
create_time = Date.civil(2011, 9, 29)
|
14
|
+
customer.meta_data.create_time.year.should == create_time.year
|
15
|
+
customer.addresses.count.should == 2
|
16
|
+
|
17
|
+
customer.addresses.first.line1.should == "123 Main St."
|
18
|
+
|
19
|
+
customer.phones.size.should == 2
|
20
|
+
customer.phones.first.free_form_number.should == "(408) 555-1212"
|
21
|
+
|
22
|
+
customer.email.address.should == "johndoe@gmail.com"
|
23
|
+
|
24
|
+
customer.notes.count.should == 1
|
25
|
+
customer.notes.first.content.should == "Likes chocolate and horses"
|
26
|
+
|
27
|
+
customer.custom_fields.count.should == 3
|
28
|
+
|
29
|
+
customer.open_balance.amount.should == 6200.0
|
30
|
+
end
|
31
|
+
|
32
|
+
it "can assign an email address" do
|
33
|
+
customer = Quickeebooks::Online::Model::Customer.new
|
34
|
+
the_email = "foo@example.org"
|
35
|
+
customer.email_address = the_email
|
36
|
+
customer.email.is_a?(Quickeebooks::Online::Model::Email).should == true
|
37
|
+
customer.email.address.should == the_email
|
38
|
+
end
|
39
|
+
|
40
|
+
it "cannot update an invalid model" do
|
41
|
+
customer = Quickeebooks::Online::Model::Customer.new
|
42
|
+
customer.valid_for_update?.should == false
|
43
|
+
customer.errors.keys.include?(:sync_token).should == true
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
require "quickeebooks/online/model/invoice"
|
4
|
+
|
5
|
+
describe "Quickeebooks::Online::Model::Invoice" do
|
6
|
+
|
7
|
+
describe "parse invoice from XML" do
|
8
|
+
xml = File.read(File.dirname(__FILE__) + "/../../xml/online/invoice.xml")
|
9
|
+
invoice = Quickeebooks::Online::Model::Invoice.from_xml(xml)
|
10
|
+
invoice.id.should == 28
|
11
|
+
invoice.line_items.count.should == 1
|
12
|
+
invoice.line_items.first.unit_price.should == 100
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "fakeweb"
|
3
|
+
require "oauth"
|
4
|
+
require "quickeebooks/online/service/account"
|
5
|
+
|
6
|
+
describe "Quickeebooks::Online::Service::Account" do
|
7
|
+
before(:all) do
|
8
|
+
FakeWeb.allow_net_connect = false
|
9
|
+
qb_key = "key"
|
10
|
+
qb_secret = "secreet"
|
11
|
+
|
12
|
+
@realm_id = "9991111222"
|
13
|
+
@base_uri = "https://qbo.intuit.com/qbo36"
|
14
|
+
@oauth_consumer = OAuth::Consumer.new(qb_key, qb_key, {
|
15
|
+
:site => "https://oauth.intuit.com",
|
16
|
+
:request_token_path => "/oauth/v1/get_request_token",
|
17
|
+
:authorize_path => "/oauth/v1/get_access_token",
|
18
|
+
:access_token_path => "/oauth/v1/get_access_token"
|
19
|
+
})
|
20
|
+
@oauth = OAuth::AccessToken.new(@oauth_consumer, "blah", "blah")
|
21
|
+
end
|
22
|
+
|
23
|
+
it "receives 404 from invalid base URL" do
|
24
|
+
uri = "https://qbo.intuit.com/invalid"
|
25
|
+
service = Quickeebooks::Online::Service::Account.new(@oauth, @realm_id, uri)
|
26
|
+
FakeWeb.register_uri(:post, service.url_for_resource("accounts"), :status => ["200", "OK"], :body => "blah")
|
27
|
+
lambda { service.list }.should raise_error(IntuitRequestException)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "can fetch a list of accounts" do
|
31
|
+
xml = File.read(File.dirname(__FILE__) + "/../../../xml/online/accounts.xml")
|
32
|
+
service = Quickeebooks::Online::Service::Account.new(@oauth, @realm_id, @base_uri)
|
33
|
+
FakeWeb.register_uri(:post, service.url_for_resource("accounts"), :status => ["200", "OK"], :body => xml)
|
34
|
+
accounts = service.list
|
35
|
+
accounts.current_page.should == 1
|
36
|
+
accounts.entries.count.should == 10
|
37
|
+
accounts.entries.first.current_balance.should == 6200
|
38
|
+
end
|
39
|
+
|
40
|
+
it "can create an account" do
|
41
|
+
xml = File.read(File.dirname(__FILE__) + "/../../../xml/online/account.xml")
|
42
|
+
service = Quickeebooks::Online::Service::Account.new(@oauth, @realm_id, @base_uri)
|
43
|
+
FakeWeb.register_uri(:post, service.url_for_resource("account"), :status => ["200", "OK"], :body => xml)
|
44
|
+
account = Quickeebooks::Online::Model::Account.new
|
45
|
+
account.name = "Billy Bob"
|
46
|
+
account.sub_type = "AccountsPayable"
|
47
|
+
account.valid?.should == true
|
48
|
+
result = service.create(account)
|
49
|
+
result.id.to_i.should > 0
|
50
|
+
end
|
51
|
+
|
52
|
+
it "can delete an account" do
|
53
|
+
service = Quickeebooks::Online::Service::Account.new(@oauth, @realm_id, @base_uri)
|
54
|
+
url = "#{service.url_for_resource("account")}/99?methodx=delete"
|
55
|
+
FakeWeb.register_uri(:post, url, :status => ["200", "OK"])
|
56
|
+
account = Quickeebooks::Online::Model::Account.new
|
57
|
+
account.id = 99
|
58
|
+
account.sync_token = 0
|
59
|
+
result = service.delete(account)
|
60
|
+
result.should == true
|
61
|
+
end
|
62
|
+
|
63
|
+
it "cannot delete an account with missing required fields for deletion" do
|
64
|
+
service = Quickeebooks::Online::Service::Account.new(@oauth, @realm_id, @base_uri)
|
65
|
+
account = Quickeebooks::Online::Model::Account.new
|
66
|
+
lambda { service.delete(account) }.should raise_error(InvalidModelException, "Missing required parameters for delete")
|
67
|
+
end
|
68
|
+
|
69
|
+
it "exception is raised when we try to create an invalid account" do
|
70
|
+
account = Quickeebooks::Online::Model::Account.new
|
71
|
+
service = Quickeebooks::Online::Service::Account.new(@oauth, @realm_id, @base_uri)
|
72
|
+
lambda { service.create(account) }.should raise_error(InvalidModelException)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "can fetch an account by id" do
|
76
|
+
xml = File.read(File.dirname(__FILE__) + "/../../../xml/online/account.xml")
|
77
|
+
service = Quickeebooks::Online::Service::Account.new(@oauth, @realm_id, @base_uri)
|
78
|
+
url = "#{service.url_for_resource("account")}/99"
|
79
|
+
FakeWeb.register_uri(:get, url, :status => ["200", "OK"], :body => xml)
|
80
|
+
account = service.fetch_by_id(99)
|
81
|
+
account.name.should == "Billy Bob"
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|