X Tutup
Skip to content

Commit beb3abd

Browse files
committed
Updates.
1 parent 77469ff commit beb3abd

File tree

17 files changed

+757
-767
lines changed

17 files changed

+757
-767
lines changed

README.md

Lines changed: 55 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,130 +1,94 @@
1-
# quickbooks-python
1+
# python-quickbooks
22
-------------------
33

4-
A really simple, brute-force, Python class for accessing the Quickbooks API.
4+
A simple Python class for accessing the Quickbooks API.
5+
Complete rework of [quickbooks-python](https://github.com/troolee/quickbooks-python).
56

6-
It was made to work alongside Django, but should work without it.
7+
These instructions were written for a Django application. Make sure to change it to whatever framework/method you're using.
78

8-
Made much simpler with some major contributions from @HaPsantran. See HaPsantran's branch [here](https://github.com/HaPsantran/quickbooks-python). I've cleaned the script up a bit for a semi-clean v0.1.0.
9+
## Connecting your application to Quickbooks Online
910

10-
As HaPsantran says in their ReadMe:
11+
1. Create the Authorization URL your application:
1112

12-
>Generally when using this module (or any of the QBO v3 API wrappers out there), keep in mind that there are some glaring omissions in it's functionality that (AFAIK) no one is able to get around programmatically. For example, you can't access (or create, update, or delete, obvi) Deposits or Transfers.
13+
quickbooks = QuickBooks(
14+
sandbox=DEBUG,
15+
consumer_key=QUICKBOOKS_CLIENT_KEY,
16+
consumer_secret=QUICKBOOKS_CLIENT_SECRET,
17+
callback_url=CALLBACK_URL
18+
)
1319

14-
### New in v0.2
20+
authorize_url = quickbooks.get_authorize_url()
1521

16-
* Pip package
22+
Store the authorize_url, request_token, and request_token_secret for use in the Callback method.
1723

18-
### New in v0.1.1
24+
2. Handle the callback:
1925

20-
* Well, versioning :)
21-
* Removed a lot of extraneous method calls that have essentially been replaced with query_object().
22-
* Brought in some pushes from various lovely folks.
26+
quickbooks = QuickBooks(
27+
sandbox=DEBUG,
28+
consumer_key=QUICKBOOKS_CLIENT_KEY,
29+
consumer_secret=QUICKBOOKS_CLIENT_SECRET,
30+
callback_url=CALLBACK_URL
31+
)
32+
33+
quickbooks.authorize_url = authorize_url
34+
quickbooks.request_token = request_token
35+
quickbooks.request_token_secret = request_token_secret
36+
quickbooks.set_up_service()
37+
38+
quickbooks.get_access_tokens(request.GET['oauth_verifier'])
39+
40+
realm_id = request.GET['realmId']
41+
access_token = quickbooks.access_token
42+
access_token_secret = quickbooks.access_token_secret
2343

24-
## Running the script
44+
Store realm_id, access_token, and access_token_secret need to be stored for later use.
2545

26-
Works like any Python module, but you'll need [rauth](http://rauth.readthedocs.org/en/latest/).
2746

28-
## Getting Access.
47+
## Accessing the API
2948

30-
These instructions were written for a Django application. Make sure to change it to whatever framework/method you're using.
49+
Setup the client connection using the stored `access_token` and the `access_token_secret` and `realm_id`:
3150

32-
1. Make sure you have set up your Quickbooks App. You can check whether you have on their [Manage](https://developer.intuit.com/Application/List) page. If you need help doing that, look at [their documentation](https://developer.intuit.com/docs/0025_quickbooksapi/0010_getting_started/0020_connect/0010_from_within_your_app#Implement_the_OAuth_Authorization_Workflow) <- Have fun, this page only works in Firefox.
3351

34-
2. When your callback method gets triggered, set up a QuickBooks object, and get a URL for authorization, and then access it:
35-
```
36-
qbObject = QuickBooks(
37-
consumer_key = QB_OAUTH_CONSUMER_KEY,
38-
consumer_secret = QB_OAUTH_CONSUMER_SECRET,
39-
callback_url = QB_OAUTH_CALLBACK_URL,
52+
client = QuickBooks(
53+
sandbox=True,
54+
consumer_key=QUICKBOOKS_CLIENT_KEY,
55+
consumer_secret=QUICKBOOKS_CLIENT_SECRET,
56+
access_token=access_token,
57+
access_token_secret=access_token_secret,
58+
company_id=realm_id
4059
)
4160

42-
authorize_url = qbObject.get_authorize_url() # will create a service, and further set up the qbObject.
43-
44-
# access URL, however you want to
45-
```
46-
3. Access the existing `qbObject`, fetch the `oauth_verifier` and `realmId` from the URL, and set up a session (`request` is Django's [`HttpRequest`](https://docs.djangoproject.com/en/dev/ref/request-response/) object):
47-
```
48-
oauth_token = request.GET['oauth_token']
49-
oauth_verifier = request.GET['oauth_verifier']
50-
realm_id = request.GET['realmId']
51-
52-
session = qbObject.get_access_tokens(oauth_verifier)
53-
54-
# say you want access to the employees.
55-
url = "https://qbo.intuit.com/qbo1/"
56-
url += "resource/employees/v2/%s" % (realm_id)
57-
58-
r = session.request( #This is just a Rauth request
59-
"POST",
60-
url,
61-
header_auth = True,
62-
realm = realm_id,
63-
params={"format":"json"}
64-
)
65-
```
66-
4. Store the `access_token` and the `access_token_secret` and `realm_id`, use them whenever you want to set up a new QB Object:
67-
68-
```
69-
qb = QuickBooks(
70-
consumer_key = QB_OAUTH_CONSUMER_KEY,
71-
consumer_secret = QB_OAUTH_CONSUMER_SECRET,
72-
access_token = qbtoken.access_token, # the stored token
73-
access_token_secret = qbtoken.access_token_secret, # the stored secret
74-
company_id = qbtoken.realm_id #the stored realm_id
75-
)
76-
```
77-
## Accessing the API
7861

79-
Once you've gotten a hold of your QuickBooks access tokens, you can create a QB object:
62+
List of objects:
8063

81-
qb = QuickBooks(consumer_key = QB_OAUTH_CONSUMER_KEY,
82-
consumer_secret = QB_OAUTH_CONSUMER_SECRET,
83-
access_token = QB_ACCESS_TOKEN,
84-
access_token_secret = QB_ACCESS_TOKEN_SECRET,
85-
company_id = QB_REALM_ID
86-
)
8764

88-
__Note:__ This is a work-in-progress. It was made public to help other developers access the QuickBooks API, it's not a guarantee that it will ever be finished.
65+
customers = Customer.all()
8966

90-
## Available methods
9167

92-
you can access any object via the query_object method.
68+
Filtered list of objects:
9369

94-
qb.query_objects(business_object, params, query_tail)
9570

96-
The available business objects are:
71+
customer = Customer.filter(Active=True)
9772

98-
"Account","Attachable","Bill","BillPayment",
99-
"Class","CompanyInfo","CreditMemo","Customer",
100-
"Department","Employee","Estimate","Invoice",
101-
"Item","JournalEntry","Payment","PaymentMethod",
102-
"Preferences","Purchase","PurchaseOrder",
103-
"SalesReceipt","TaxCode","TaxRate","Term",
104-
"TimeActivity","Vendor","VendorCredit"
10573

106-
Example:
74+
Get single object by Id and update:
10775

108-
qb.query_objects("Bill")
109-
> [{u'DocNumber': ... }]
11076

77+
customer = Customer.get(1)
78+
customer.CompanyName = "New Test Company Name"
79+
customer.save()
11180

11281

113-
## From HaPsantran's README
114-
------------------
11582

116-
Update: As I try using the pnl function in report.py, I notice that not all of the activity is making it in. I have to assume it basically doesn't work then. Rather than rebuild it, though, I'm probably going to use other tools outside the module to massage the ledger_lines I get out of massage.py (rather than build special reporting tools within the quickbooks package).
83+
Create new object:
11784

118-
Intuit has promised reporting features, but who knows...
11985

120-
http://stackoverflow.com/questions/19455750/quickbooks-online-api-financial-data
86+
customer = Customer()
87+
customer.CompanyName = "Test Company"
88+
customer.save()
12189

122-
### License
12390

124-
The MIT License (MIT)
12591

126-
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
92+
__Note:__ This is a work-in-progress. It was made public to help other developers access the QuickBooks API, it's not a guarantee that it will ever be finished.
12793

128-
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12994

130-
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

quickbooks/client.py

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
import json
21
import httplib
3-
import urllib
42

53
from exceptions import QuickbooksException, SevereException
64

@@ -11,14 +9,6 @@
119
print("http://rauth.readthedocs.org/en/latest/\n")
1210

1311

14-
sandbox_api_url_v3 = "https://sandbox-quickbooks.api.intuit.com/v3"
15-
api_url_v3 = "https://quickbooks.api.intuit.com/v3"
16-
17-
request_token_url = "https://oauth.intuit.com/oauth/v1/get_request_token"
18-
access_token_url = "https://oauth.intuit.com/oauth/v1/get_access_token"
19-
20-
authorize_url = "https://appcenter.intuit.com/Connect/Begin"
21-
2212

2313
class QuickBooks(object):
2414
"""A wrapper class around Python's Rauth module for Quickbooks the API"""
@@ -35,6 +25,14 @@ class QuickBooks(object):
3525

3626
qbService = None
3727

28+
sandbox_api_url_v3 = "https://sandbox-quickbooks.api.intuit.com/v3"
29+
api_url_v3 = "https://quickbooks.api.intuit.com/v3"
30+
31+
request_token_url = "https://oauth.intuit.com/oauth/v1/get_request_token"
32+
access_token_url = "https://oauth.intuit.com/oauth/v1/get_access_token"
33+
34+
authorize_url = "https://appcenter.intuit.com/Connect/Begin"
35+
3836
request_token = ''
3937
request_token_secret = ''
4038

@@ -83,9 +81,9 @@ def __new__(cls, **args):
8381
@property
8482
def api_url(self):
8583
if self.sandbox:
86-
return sandbox_api_url_v3
84+
return self.sandbox_api_url_v3
8785
else:
88-
return api_url_v3
86+
return self.api_url_v3
8987

9088
def create_session(self):
9189
if self.consumer_secret and self.consumer_key and self.access_token_secret and self.access_token:
@@ -97,7 +95,8 @@ def create_session(self):
9795
)
9896
self.session = session
9997
else:
100-
raise QuickbooksException("Need four creds for Quickbooks.create_session.")
98+
raise QuickbooksException("Quickbooks authenication fields not set. Cannot create session.")
99+
101100
return self.session
102101

103102
def get_authorize_url(self):
@@ -111,25 +110,23 @@ def get_authorize_url(self):
111110
self.request_token, self.request_token_secret = self.qbService.get_request_token(
112111
params={'oauth_callback': self.callback_url})
113112

114-
print self.request_token, self.request_token_secret
115-
116113
return self.qbService.get_authorize_url(self.request_token)
117114

118115
def set_up_service(self):
119116
self.qbService = OAuth1Service(
120117
name=None,
121118
consumer_key=self.consumer_key,
122119
consumer_secret=self.consumer_secret,
123-
request_token_url=request_token_url,
124-
access_token_url=access_token_url,
125-
authorize_url=authorize_url,
120+
request_token_url=self.request_token_url,
121+
access_token_url=self.access_token_url,
122+
authorize_url=self.authorize_url,
126123
base_url=None
127124
)
128125

129126
def get_access_tokens(self, oauth_verifier):
130127
"""
131-
Wrapper around get_auth_session, returns session, and sets
132-
access_token and access_token_secret on the QB Object.
128+
Wrapper around get_auth_session, returns session, and sets access_token and
129+
access_token_secret on the QB Object.
133130
:param oauth_verifier: the oauth_verifier as specified by OAuth 1.0a
134131
"""
135132
session = self.qbService.get_auth_session(
@@ -157,6 +154,10 @@ def make_request(self, request_type, url, request_body=None, content_type='appli
157154
req = self.session.request(request_type, url, True, self.company_id, headers=headers, data=request_body)
158155
result = req.json()
159156

157+
print "----------------"
158+
print result
159+
print "----------------"
160+
160161
if req.status_code is not httplib.OK or "Fault" in result:
161162
self.handle_exceptions(result["Fault"])
162163
else:
@@ -173,8 +174,14 @@ def handle_exceptions(self, results):
173174
for error in results["Error"]:
174175

175176
message = error["Message"]
176-
code = error["code"]
177-
detail = error["Detail"]
177+
178+
detail = ""
179+
if "Detail" in error:
180+
detail = error["Detail"]
181+
182+
code = ""
183+
if "code" in error:
184+
code = error["code"]
178185

179186
if code >= 10000:
180187
raise SevereException(message, code, detail)
@@ -193,6 +200,7 @@ def get_all(self, qbbo):
193200
select = "select * from {0}".format(qbbo.lower())
194201

195202
url = "{0}/company/{1}/query".format(self.api_url, self.company_id)
203+
#result = self.make_request("GET", url, select, content_type='text/plain')
196204
result = self.make_request("POST", url, select, content_type='application/text')
197205

198206
return result
@@ -222,12 +230,14 @@ def isvalid_object_name(self, object_name):
222230
return True
223231

224232
def update_object(self, qbbo, request_body):
225-
# see this link for url troubleshooting info:
226-
# http://stackoverflow.com/questions/23333300/whats-the-correct-uri-
227-
# for-qbo-v3-api-update-operation/23340464#23340464
228-
229233
url = self.api_url + "/company/{0}/{1}".format(self.company_id, qbbo.lower())
230234
result = self.make_request("POST", url, request_body)
231235

232236
return result
233237

238+
239+
# def test(self):
240+
# table = Table("Customer")
241+
#
242+
# select = table.select()
243+
# select.where

quickbooks/mixins.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,17 @@ def from_json(cls, json_data):
1515
for key in json_data:
1616
if key in obj.class_dict:
1717
sub_obj = obj.class_dict[key]()
18-
sub_obj.from_json(json_data[key])
18+
sub_obj = sub_obj.from_json(json_data[key])
1919
setattr(obj, key, sub_obj)
20+
elif key in obj.list_dict:
21+
sub_list = []
22+
23+
for data in json_data[key]:
24+
sub_obj = obj.list_dict[key]()
25+
sub_obj = sub_obj.from_json(data)
26+
sub_list.append(sub_obj)
27+
28+
setattr(obj, key, sub_list)
2029
else:
2130
setattr(obj, key, json_data[key])
2231

@@ -47,8 +56,10 @@ def save(self):
4756
else:
4857
json_data = qb.create_object(self.qbo_object_name, self.to_json())
4958

50-
self = type(self).from_json(json_data[self.qbo_object_name])
51-
return self
59+
obj = type(self).from_json(json_data[self.qbo_object_name])
60+
self.Id = obj.Id
61+
62+
return obj
5263

5364

5465
class ListMixin(object):

0 commit comments

Comments
 (0)
X Tutup