quickeebooks 0.0.8 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +1 -1
- data/README.md +93 -15
- data/lib/quickeebooks/online/model/invoice_header.rb +1 -0
- data/lib/quickeebooks/version.rb +1 -1
- data/lib/quickeebooks/windows/model/address.rb +1 -1
- data/lib/quickeebooks/windows/model/customer.rb +1 -1
- data/lib/quickeebooks/windows/model/phone.rb +1 -1
- data/lib/quickeebooks/windows/service/customer.rb +34 -0
- data/spec/quickeebooks/windows/services/customer_spec.rb +20 -0
- data/spec/xml/windows/customer_update_success.xml +13 -0
- data/tmp/create_customer_windows.rb +19 -1
- metadata +3 -2
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -22,22 +22,69 @@ Gems:
|
|
22
22
|
* `nokogiri` : XML parsing
|
23
23
|
* `active_model` : For validations
|
24
24
|
|
25
|
-
## Getting Started
|
25
|
+
## Getting Started & Initiating Authentication Flow with Intuit
|
26
26
|
|
27
|
-
|
27
|
+
What follows is an example using Rails but the principles can be adapted to any other framework / pure Ruby.
|
28
|
+
|
29
|
+
Create a Rails initializer with:
|
28
30
|
|
29
31
|
```ruby
|
30
|
-
QB_KEY = "your
|
31
|
-
QB_SECRET = "your
|
32
|
+
QB_KEY = "your apps Intuit App Key"
|
33
|
+
QB_SECRET = "your apps Intuit Secret Key"
|
32
34
|
|
33
|
-
qb_oauth_consumer = OAuth::Consumer.new(QB_KEY, QB_SECRET, {
|
35
|
+
$qb_oauth_consumer = OAuth::Consumer.new(QB_KEY, QB_SECRET, {
|
34
36
|
:site => "https://oauth.intuit.com",
|
35
37
|
:request_token_path => "/oauth/v1/get_request_token",
|
36
38
|
:authorize_url => "https://appcenter.intuit.com/Connect/Begin",
|
37
39
|
:access_token_path => "/oauth/v1/get_access_token"
|
38
40
|
})
|
41
|
+
```
|
42
|
+
|
43
|
+
To start the authentication flow with Intuit you include the Intuit Javascript and on a page of your choosing you present the "Connect to Quickbooks" button by including this XHTML:
|
44
|
+
|
45
|
+
|
46
|
+
```HTML
|
47
|
+
<!-- somewhere in your document include the Javascript -->
|
48
|
+
<script type="text/javascript" src="https://appcenter.intuit.com/Content/IA/intuit.ipp.anywhere.js"></script>
|
49
|
+
|
50
|
+
<!-- configure the Intuit object: 'grantUrl' is a URL in your application which kicks off the flow, see below -->
|
51
|
+
<script>
|
52
|
+
intuit.ipp.anywhere.setup({menuProxy: '/path/to/blue-dot', grantUrl: '/path/to/your-flow-start'});
|
53
|
+
</script>
|
54
|
+
|
55
|
+
<!-- this will display a button that the user clicks to start the flow -->
|
56
|
+
<ipp:connectToIntuit></ipp:connectToIntuit>
|
57
|
+
```
|
58
|
+
|
59
|
+
Your Controller action (the `grantUrl` above) should look like this:
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
def authenticate
|
63
|
+
callback = quickbooks_oauth_callback_url
|
64
|
+
token = $qb_oauth_consumer.get_request_token(:oauth_callback => callback)
|
65
|
+
session[:qb_request_token] = token
|
66
|
+
redirect_to("https://appcenter.intuit.com/Connect/Begin?oauth_token=#{token.token}") and return
|
67
|
+
end
|
68
|
+
```
|
69
|
+
|
70
|
+
Where `quickbooks_oauth_callback_url` is the absolute URL of your application that Intuit should send the user when authentication succeeeds. That action should look like:
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
def oauth_callback
|
74
|
+
at = session[:qb_request_token].get_access_token(:oauth_verifier => params[:oauth_verifier])
|
75
|
+
token = at.token
|
76
|
+
secret = at.secret
|
77
|
+
realm_id = params['realmId']
|
78
|
+
# store the token, secret & RealmID somewhere for this user, you will need all 3 to work with Quickeebooks
|
79
|
+
end
|
80
|
+
```
|
39
81
|
|
40
|
-
|
82
|
+
## Creating an OAuth Access Token
|
83
|
+
|
84
|
+
Once you have your users OAuth Token & Secret you can initialize your `OAuth Consumer` and create a `OAuth Client` using the `$qb_oauth_consumer` you created earlier in your Rails initializer:
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
oauth_client = OAuth::AccessToken.new($qb_oauth_consumer, access_token, access_secret)
|
41
88
|
```
|
42
89
|
|
43
90
|
## Quickbooks Online vs Windows
|
@@ -46,17 +93,21 @@ IDS provides 2 APIs, one for interacting with Quickbooks Online resources and on
|
|
46
93
|
|
47
94
|
See: https://ipp.developer.intuit.com/0010_Intuit_Partner_Platform/0050_Data_Services
|
48
95
|
|
49
|
-
|
96
|
+
You will need to be aware of which flavor of the API you want to invoke.
|
50
97
|
|
51
98
|
For example:
|
52
99
|
|
53
100
|
```ruby
|
54
101
|
# Instantiate a Online API
|
55
|
-
customer_service = Quickeebooks::Online::Service::Customer.new
|
102
|
+
customer_service = Quickeebooks::Online::Service::Customer.new
|
103
|
+
customer_service.access_token = oauth_client
|
104
|
+
customer_service.realm_id = realm_id
|
56
105
|
customer_service.list
|
57
106
|
|
58
107
|
# Instantiate a Windows API
|
59
108
|
customer_service = Quickeebooks::Windows::Service::Customer.new(oauth_client, realm_id)
|
109
|
+
customer_service.access_token = oauth_client
|
110
|
+
customer_service.realm_id = realm_id
|
60
111
|
customer_service.list
|
61
112
|
```
|
62
113
|
|
@@ -65,7 +116,10 @@ All of the documentation below is geared towards the Online flavor but unless no
|
|
65
116
|
Now we can initialize any of the `Service` clients:
|
66
117
|
|
67
118
|
```ruby
|
68
|
-
customer_service = Quickeebooks::Online::Service::Customer.new
|
119
|
+
customer_service = Quickeebooks::Online::Service::Customer.new
|
120
|
+
customer_service.access_token = oauth_client
|
121
|
+
customer_service.realm_id = realm_id
|
122
|
+
customer_service.list
|
69
123
|
customer_service.list
|
70
124
|
|
71
125
|
# returns a `Collection` object
|
@@ -99,7 +153,11 @@ Pass a `Sort` object for any desired sorting or just let Intuit use the default
|
|
99
153
|
Specify none of these to get the defaults:
|
100
154
|
|
101
155
|
```ruby
|
102
|
-
customer_service = Quickeebooks::Online::Service::Customer.new
|
156
|
+
customer_service = Quickeebooks::Online::Service::Customer.new
|
157
|
+
customer_service.access_token = oauth_client
|
158
|
+
customer_service.realm_id = realm_id
|
159
|
+
customer_service.list
|
160
|
+
|
103
161
|
# fetch all customers with default parameters (pagination, sorting, filtering)
|
104
162
|
customers = customer_service.list
|
105
163
|
```
|
@@ -172,7 +230,11 @@ filters = []
|
|
172
230
|
filters << Quickeebooks::Online::Service::Filter.new(:text, :field => 'FamilyName', :value => 'Richards')
|
173
231
|
datetime = Time.mktime(2011, 2, 25)
|
174
232
|
filters << Quickeebooks::Online::Service::Filter.new(:datetime, :field => 'CreateTime', :before => datetime)
|
175
|
-
customer_service = Quickeebooks::Online::Service::Customer.new
|
233
|
+
customer_service = Quickeebooks::Online::Service::Customer.new
|
234
|
+
customer_service.access_token = oauth_client
|
235
|
+
customer_service.realm_id = realm_id
|
236
|
+
customer_service.list
|
237
|
+
|
176
238
|
customers = customer_service.list(filters)
|
177
239
|
```
|
178
240
|
## Sorting (currently only supported in the Online API)
|
@@ -201,7 +263,11 @@ filters << Quickeebooks::Online::Service::Filter.new(:datetime, :field => 'Creat
|
|
201
263
|
|
202
264
|
sorter = Quickeebooks::Online::Service::Sort.new('FamilyName', 'AtoZ')
|
203
265
|
|
204
|
-
customer_service = Quickeebooks::Online::Service::Customer.new
|
266
|
+
customer_service = Quickeebooks::Online::Service::Customer.new
|
267
|
+
customer_service.access_token = oauth_client
|
268
|
+
customer_service.realm_id = realm_id
|
269
|
+
customer_service.list
|
270
|
+
|
205
271
|
customers = customer_service.list(filters, 1, 30, sort)
|
206
272
|
|
207
273
|
# returns
|
@@ -222,7 +288,11 @@ Use the `Service` instance to fetch an object by its id using the `fetch_by_id`
|
|
222
288
|
|
223
289
|
```ruby
|
224
290
|
# fetch the Customer object with an id of 100
|
225
|
-
customer_service = Quickeebooks::Online::Service::Customer.new
|
291
|
+
customer_service = Quickeebooks::Online::Service::Customer.new
|
292
|
+
customer_service.access_token = oauth_client
|
293
|
+
customer_service.realm_id = realm_id
|
294
|
+
customer_service.list
|
295
|
+
|
226
296
|
customer = customer_service.fetch_by_id(100)
|
227
297
|
customer.name
|
228
298
|
=> John Doe
|
@@ -239,7 +309,11 @@ You will need make sure you supply all required fields for that Intuit object, s
|
|
239
309
|
Pass an instance of your object to the `create` method on its related Service:
|
240
310
|
|
241
311
|
```ruby
|
242
|
-
customer_service = Quickeebooks::Online::Service::Customer.new
|
312
|
+
customer_service = Quickeebooks::Online::Service::Customer.new
|
313
|
+
customer_service.access_token = oauth_client
|
314
|
+
customer_service.realm_id = realm_id
|
315
|
+
customer_service.list
|
316
|
+
|
243
317
|
customer = Quickeebooks::Online::Model::Customer.new
|
244
318
|
customer.name = "Richard Parker"
|
245
319
|
customer.email = "richard@example.org"
|
@@ -253,7 +327,11 @@ customer_service.create(customer)
|
|
253
327
|
Pass an instance of your object to the `update` method on its related Service:
|
254
328
|
|
255
329
|
```ruby
|
256
|
-
customer_service = Quickeebooks::Online::Service::Customer.new
|
330
|
+
customer_service = Quickeebooks::Online::Service::Customer.new
|
331
|
+
customer_service.access_token = oauth_client
|
332
|
+
customer_service.realm_id = realm_id
|
333
|
+
customer_service.list
|
334
|
+
|
257
335
|
customer = customer_service.fetch_by_id(100)
|
258
336
|
customer.name = "Richard Parker"
|
259
337
|
customer.email = "richard@example.org"
|
@@ -17,6 +17,7 @@ module Quickeebooks
|
|
17
17
|
xml_accessor :sales_tax_code_name, :from => 'SalesTaxCodeName'
|
18
18
|
xml_accessor :sub_total_amount, :from => 'SubTotalAmt', :as => Float
|
19
19
|
xml_accessor :tax_rate, :from => 'TaxRate', :as => Float
|
20
|
+
xml_accessor :tax_amount, :from => 'TaxAmt', :as => Float
|
20
21
|
xml_accessor :balance, :from => 'Balance', :as => Float
|
21
22
|
xml_accessor :total_amount, :from => 'TotalAmt', :as => Float
|
22
23
|
xml_accessor :due_date, :from => 'DueDate', :as => Time
|
data/lib/quickeebooks/version.rb
CHANGED
@@ -15,8 +15,8 @@ module Quickeebooks
|
|
15
15
|
xml_accessor :country_sub_division_code, :from => 'CountrySubDivisionCode'
|
16
16
|
xml_accessor :postal_code, :from => 'PostalCode'
|
17
17
|
xml_accessor :postal_code_suffix, :from => 'PostalCodeSuffix'
|
18
|
-
xml_accessor :tag, :from => 'Tag'
|
19
18
|
xml_accessor :default, :from => 'Default'
|
19
|
+
xml_accessor :tag, :from => 'Tag'
|
20
20
|
|
21
21
|
def zip
|
22
22
|
postal_code
|
@@ -34,9 +34,9 @@ module Quickeebooks
|
|
34
34
|
xml_accessor :type_of, :from => 'TypeOf'
|
35
35
|
xml_accessor :name, :from => 'Name'
|
36
36
|
xml_accessor :addresses, :from => 'Address', :as => [Quickeebooks::Windows::Model::Address]
|
37
|
-
xml_accessor :email, :from => 'Email', :as => Quickeebooks::Windows::Model::Email
|
38
37
|
xml_accessor :phones, :from => 'Phone', :as => [Quickeebooks::Windows::Model::Phone]
|
39
38
|
xml_accessor :web_site, :from => 'WebSite', :as => Quickeebooks::Windows::Model::WebSite
|
39
|
+
xml_accessor :email, :from => 'Email', :as => Quickeebooks::Windows::Model::Email
|
40
40
|
xml_accessor :title, :from => 'Title'
|
41
41
|
xml_accessor :given_name, :from => 'GivenName'
|
42
42
|
xml_accessor :middle_name, :from => 'MiddleName'
|
@@ -7,8 +7,8 @@ module Quickeebooks
|
|
7
7
|
xml_accessor :id, :from => 'Id'
|
8
8
|
xml_accessor :device_type, :from => 'DeviceType'
|
9
9
|
xml_accessor :free_form_number, :from => 'FreeFormNumber'
|
10
|
-
xml_accessor :tag, :from => 'Tag'
|
11
10
|
xml_accessor :default, :from => 'Default'
|
11
|
+
xml_accessor :tag, :from => 'Tag'
|
12
12
|
|
13
13
|
def default?
|
14
14
|
default == "true"
|
@@ -32,7 +32,41 @@ module Quickeebooks
|
|
32
32
|
</Add>
|
33
33
|
XML
|
34
34
|
perform_write(Quickeebooks::Windows::Model::Customer, xml)
|
35
|
+
end
|
36
|
+
|
37
|
+
def update(customer)
|
38
|
+
# XML is a wrapped 'object' where the type is specified as an attribute
|
39
|
+
# <Object xsi:type="Invoice">
|
40
|
+
|
41
|
+
# Intuit requires that some fields are unset / do not exist.
|
42
|
+
customer.meta_data = nil
|
43
|
+
customer.external_key = nil
|
44
|
+
|
45
|
+
# unset Id fields in addresses, phones, email
|
46
|
+
if customer.addresses
|
47
|
+
customer.addresses.each {|address| address.id = nil }
|
48
|
+
end
|
49
|
+
if customer.email
|
50
|
+
customer.email.id = nil
|
51
|
+
end
|
52
|
+
|
53
|
+
if customer.phones
|
54
|
+
customer.phones.each {|phone| phone.id = nil }
|
55
|
+
end
|
56
|
+
|
57
|
+
if customer.web_site
|
58
|
+
customer.web_site.id = nil
|
59
|
+
end
|
35
60
|
|
61
|
+
xml_node = customer.to_xml(:name => 'Object')
|
62
|
+
xml_node.set_attribute('xsi:type', 'Customer')
|
63
|
+
xml = <<-XML
|
64
|
+
<Mod xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" RequestId="#{guid}" xmlns="http://www.intuit.com/sb/cdm/v2">
|
65
|
+
<ExternalRealmId>#{self.realm_id}</ExternalRealmId>
|
66
|
+
#{xml_node}
|
67
|
+
</Mod>
|
68
|
+
XML
|
69
|
+
perform_write(Quickeebooks::Windows::Model::Customer, xml)
|
36
70
|
end
|
37
71
|
|
38
72
|
end
|
@@ -153,6 +153,26 @@ describe "Quickeebooks::Windows::Service::Customer" do
|
|
153
153
|
create_response.success.party_role_ref.id.value.should == "6762304"
|
154
154
|
create_response.success.request_name.should == "CustomerAdd"
|
155
155
|
end
|
156
|
+
|
157
|
+
it "can update a customer name" do
|
158
|
+
customer_xml = File.read(File.dirname(__FILE__) + "/../../../xml/windows/customer.xml")
|
159
|
+
update_response_xml = File.read(File.dirname(__FILE__) + "/../../../xml/windows/customer_update_success.xml")
|
160
|
+
service = Quickeebooks::Windows::Service::Customer.new
|
161
|
+
model = Quickeebooks::Windows::Model::Customer
|
162
|
+
customer = model.from_xml(customer_xml)
|
163
|
+
customer.name.should == "Wine House"
|
164
|
+
|
165
|
+
service.access_token = @oauth
|
166
|
+
service.realm_id = @realm_id
|
167
|
+
FakeWeb.register_uri(:post, service.url_for_resource(model::REST_RESOURCE), :status => ["200", "OK"], :body => update_response_xml)
|
168
|
+
|
169
|
+
# change the name
|
170
|
+
customer.name = "Acme Cafe"
|
171
|
+
update_response = service.update(customer)
|
172
|
+
update_response.success?.should == true
|
173
|
+
update_response.success.party_role_ref.id.value.should == "6762304"
|
174
|
+
update_response.success.request_name.should == "CustomerMod"
|
175
|
+
end
|
156
176
|
|
157
177
|
|
158
178
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<?xml version="1.0"?>
|
2
|
+
<RestResponse xmlns="http://www.intuit.com/sb/cdm/v2">
|
3
|
+
<Success RequestId="1b405391016149c593c005e45700dd01">
|
4
|
+
<PartyRoleRef>
|
5
|
+
<Id idDomain="NG">6762304</Id>
|
6
|
+
<SyncToken>7</SyncToken>
|
7
|
+
<LastUpdatedTime>2012-11-29T01:01:47Z</LastUpdatedTime>
|
8
|
+
<PartyReferenceId idDomain="NG">6170150</PartyReferenceId>
|
9
|
+
</PartyRoleRef>
|
10
|
+
<RequestName>CustomerMod</RequestName>
|
11
|
+
<ProcessedTime>2012-11-29T01:01:47Z</ProcessedTime>
|
12
|
+
</Success>
|
13
|
+
</RestResponse>
|
@@ -3,6 +3,7 @@ require 'oauth'
|
|
3
3
|
$: << File.expand_path('./lib')
|
4
4
|
require 'quickeebooks'
|
5
5
|
|
6
|
+
# attached to "Vinosmith.qbw" in Quickbooks Simple Start 2010
|
6
7
|
consumer_key = 'qyprdL8NUDMYcCzwp8ea9KbIhaMSRk'
|
7
8
|
consumer_secret = 'FcE5kihBYMVQGvX9UNYMxsrM8mP7bfuxc36PLhJB'
|
8
9
|
qb_oauth_consumer = OAuth::Consumer.new(consumer_key, consumer_secret, {
|
@@ -22,6 +23,22 @@ service = Quickeebooks::Windows::Service::Customer.new
|
|
22
23
|
service.access_token = oauth
|
23
24
|
service.realm_id = realm_id
|
24
25
|
|
26
|
+
customer = service.fetch_by_id(1)
|
27
|
+
puts customer.name
|
28
|
+
if customer.notes
|
29
|
+
customer.notes.each { |note| puts note.content }
|
30
|
+
end
|
31
|
+
# UPDATE it
|
32
|
+
customer.name = "Cody 22 TEST"
|
33
|
+
|
34
|
+
customer.meta_data = nil
|
35
|
+
customer.external_key = nil
|
36
|
+
|
37
|
+
response = service.update(customer)
|
38
|
+
#puts response.inspect
|
39
|
+
|
40
|
+
#puts service.list
|
41
|
+
=begin
|
25
42
|
customer = Quickeebooks::Windows::Model::Customer.new
|
26
43
|
customer.type_of = "Person"
|
27
44
|
customer.name = "Cody Test-#{Time.now.to_i}"
|
@@ -38,4 +55,5 @@ billing_address.postal_code = "94117"
|
|
38
55
|
billing_address.country = "USA"
|
39
56
|
customer.address = billing_address
|
40
57
|
result = service.create(customer)
|
41
|
-
puts result.inspect
|
58
|
+
puts result.inspect
|
59
|
+
=end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: quickeebooks
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.9
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-11-29 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: roxml
|
@@ -325,6 +325,7 @@ files:
|
|
325
325
|
- spec/xml/windows/company_meta_data.xml
|
326
326
|
- spec/xml/windows/customer.xml
|
327
327
|
- spec/xml/windows/customer_create_success.xml
|
328
|
+
- spec/xml/windows/customer_update_success.xml
|
328
329
|
- spec/xml/windows/customers.xml
|
329
330
|
- spec/xml/windows/fetch_customer_by_id.xml
|
330
331
|
- spec/xml/windows/http_401.xml
|