Skip to content

Commit 787e5d3

Browse files
committed
Fixed merge conflicts.
2 parents 302b04a + 8591c7b commit 787e5d3

20 files changed

+344
-120
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ pip-delete-this-directory.txt
3636

3737
# Unit test / coverage reports
3838
htmlcov/
39+
.env
3940
.tox/
4041
.coverage
4142
.coverage.*

CHANGELOG.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
Changelog
22
========
33

4+
* 0.3.13 (May 18, 2016)
5+
* Added option to enable or disable singeton pattern (it defaults to disabled).
6+
* Improved error handling.
7+
* Added missing field CurrencyRef on BillPayment.
8+
* Fixed issue on TaxRate.
9+
10+
411
* 0.3.12 (March 18, 2016)
512
* Updated field defaults on SalesReceipt object.
613
* Updated Id field default on BillLine object.

README.rst

Lines changed: 23 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,15 @@ Connecting your application to Quickbooks Online
5858
Accessing the API
5959
-----------------
6060

61-
QuickBooks client uses a singleton pattern. Just be sure to create the
62-
QuickBooks object before you make any calls to QBO. Setup the client
61+
Create the QuickBooks client object before you make any calls to QBO. Setup the client
6362
connection using the stored ``access_token`` and the
6463
``access_token_secret`` and ``realm_id``:
6564

6665
::
6766

6867
from quickbooks import QuickBooks
6968

70-
QuickBooks(
69+
client = QuickBooks(
7170
sandbox=True,
7271
consumer_key=QUICKBOOKS_CLIENT_KEY,
7372
consumer_secret=QUICKBOOKS_CLIENT_SECRET,
@@ -81,7 +80,7 @@ details) pass in minorversion when setting up the client:
8180

8281
::
8382

84-
QuickBooks(
83+
client = QuickBooks(
8584
sandbox=True,
8685
consumer_key=QUICKBOOKS_CLIENT_KEY,
8786
consumer_secret=QUICKBOOKS_CLIENT_SECRET,
@@ -91,13 +90,19 @@ details) pass in minorversion when setting up the client:
9190
minorversion=4
9291
)
9392

93+
If your consumer_key never changes you can enable the client to stay running:
94+
95+
::
96+
97+
QuickBooks.enable_global()
98+
9499
List of objects:
95100

96101
::
97102

98103
99104
from quickbooks.objects.customer
100-
import Customer customers = Customer.all()
105+
import Customer customers = Customer.all(qb=client)
101106

102107
**Note:** The maximum number of entities that can be returned in a
103108
response is 1000. If the result size is not specified, the default
@@ -107,61 +112,61 @@ Filtered list of objects:
107112

108113
::
109114

110-
customers = Customer.filter(Active=True, FamilyName="Smith")
115+
customers = Customer.filter(Active=True, FamilyName="Smith", qb=client)
111116

112117
Filtered list of objects with paging:
113118

114119
::
115120

116-
customers = Customer.filter(start_position=1, max_results=25, Active=True, FamilyName="Smith")
121+
customers = Customer.filter(start_position=1, max_results=25, Active=True, FamilyName="Smith", qb=client)
117122

118123
List Filtered by values in list:
119124

120125
::
121126

122127
customer_names = ['Customer1', 'Customer2', 'Customer3']
123-
customers = Customer.choose(customer_names, field="DisplayName")
128+
customers = Customer.choose(customer_names, field="DisplayName", qb=client)
124129

125130
List with custom Where Clause (do not include the “WHERE”):
126131

127132
::
128133

129-
customers = Customer.where("Active = True AND CompanyName LIKE 'S%'")
134+
customers = Customer.where("Active = True AND CompanyName LIKE 'S%'", qb=client)
130135

131136
List with custom Where Clause and paging:
132137

133138
::
134139

135-
customers = Customer.where("CompanyName LIKE 'S%'", start_position=1, max_results=25)
140+
customers = Customer.where("CompanyName LIKE 'S%'", start_position=1, max_results=25, qb=client)
136141

137142
Filtering a list with a custom query (See `Intuit developer guide`_ for
138143
supported SQL statements):
139144

140145
::
141146

142-
customer = Customer.query("SELECT * FROM Customer WHERE Active = True")
147+
customer = Customer.query("SELECT * FROM Customer WHERE Active = True", qb=client)
143148

144149
Filtering a list with a custom query with paging:
145150

146151
::
147152

148-
customer = Customer.query("SELECT * FROM Customer WHERE Active = True STARTPOSITION 1 MAXRESULTS 25")
153+
customer = Customer.query("SELECT * FROM Customer WHERE Active = True STARTPOSITION 1 MAXRESULTS 25", qb=client)
149154

150155
Get single object by Id and update:
151156

152157
::
153158

154-
customer = Customer.get(1)
159+
customer = Customer.get(1, qb=client)
155160
customer.CompanyName = "New Test Company Name"
156-
customer.save()
161+
customer.save(qb=client)
157162

158163
Create new object:
159164

160165
::
161166

162167
customer = Customer()
163168
customer.CompanyName = "Test Company"
164-
customer.save()
169+
customer.save(qb=client)
165170

166171
Batch Operations
167172
----------------
@@ -178,17 +183,15 @@ Batch create a list of objects:
178183

179184
customer1 = Customer()
180185
customer1.CompanyName = "Test Company 1"
181-
customer1.save()
182186

183187
customer2 = Customer()
184188
customer2.CompanyName = "Test Company 2"
185-
customer2.save()
186189

187190
customers = []
188191
customers.append(customer1)
189192
customers.append(customer2)
190193

191-
results = batch_create(customers)
194+
results = batch_create(customers, qb=client)
192195

193196
Batch update a list of objects:
194197

@@ -200,7 +203,7 @@ Batch update a list of objects:
200203

201204
# Update customer records
202205

203-
results = batch_update(customers)
206+
results = batch_update(customers, qb=client)
204207

205208
Batch delete a list of objects:
206209

@@ -209,16 +212,8 @@ Batch delete a list of objects:
209212
from quickbooks.batch import batch_delete
210213

211214
customers = Customer.filter(Active=False)
212-
results = batch_delete(customers)
213-
214-
Batch delete a list of objects:
215+
results = batch_delete(customers, qb=client)
215216

216-
::
217-
218-
from quickbooks.batch import batch_delete
219-
220-
customers = Customer.filter(Active=False)
221-
results = batch_delete(customers)
222217

223218
Review results for batch operation:
224219

quickbooks/batch.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ def __init__(self, operation, max_request_items=30):
1414
else:
1515
raise QuickbooksException("Operation not supported.")
1616

17-
def save(self, obj_list):
17+
def save(self, obj_list, qb=None):
1818
batch_response = BatchResponse()
1919

2020
while len(obj_list) > 0:
2121
temp_list = obj_list[:self._max_request_items]
2222
obj_list = [item for item in obj_list if item not in temp_list]
23-
result = self.process_batch(temp_list)
23+
result = self.process_batch(temp_list, qb=qb)
2424

2525
batch_response.batch_responses += result.batch_responses
2626
batch_response.original_list += result.original_list
@@ -29,8 +29,9 @@ def save(self, obj_list):
2929

3030
return batch_response
3131

32-
def process_batch(self, obj_list):
33-
qb = QuickBooks()
32+
def process_batch(self, obj_list, qb=None):
33+
if not qb:
34+
qb = QuickBooks()
3435

3536
batch = self.list_to_batch_request(obj_list)
3637
json_data = qb.batch_operation(batch.to_json())
@@ -75,16 +76,16 @@ def batch_results_to_list(self, json_data, batch, original_list):
7576
return response
7677

7778

78-
def batch_create(obj_list):
79+
def batch_create(obj_list, qb=None):
7980
batch_mgr = BatchManager(BatchOperation.CREATE)
80-
return batch_mgr.save(obj_list)
81+
return batch_mgr.save(obj_list, qb=qb)
8182

8283

83-
def batch_update(obj_list):
84+
def batch_update(obj_list, qb=None):
8485
batch_mgr = BatchManager(BatchOperation.UPDATE)
85-
return batch_mgr.save(obj_list)
86+
return batch_mgr.save(obj_list, qb=qb)
8687

8788

88-
def batch_delete(obj_list):
89+
def batch_delete(obj_list, qb=None):
8990
batch_mgr = BatchManager(BatchOperation.DELETE)
90-
return batch_mgr.save(obj_list)
91+
return batch_mgr.save(obj_list, qb=qb)

quickbooks/client.py

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import httplib
66
from urlparse import parse_qsl
77

8-
from .exceptions import QuickbooksException, SevereException
8+
from .exceptions import QuickbooksException, SevereException, AuthorizationException
99

1010
try:
1111
from rauth import OAuth1Session, OAuth1Service
@@ -52,41 +52,64 @@ class QuickBooks(object):
5252
]
5353

5454
__instance = None
55+
__use_global = False
5556

5657
def __new__(cls, **kwargs):
57-
if QuickBooks.__instance is None:
58-
QuickBooks.__instance = object.__new__(cls)
58+
"""
59+
If global is disabled, don't set global client instance.
60+
"""
61+
if QuickBooks.__use_global:
62+
if QuickBooks.__instance is None:
63+
QuickBooks.__instance = object.__new__(cls)
64+
instance = QuickBooks.__instance
65+
else:
66+
instance = object.__new__(cls)
5967

6068
if 'consumer_key' in kwargs:
61-
cls.consumer_key = kwargs['consumer_key']
69+
instance.consumer_key = kwargs['consumer_key']
6270

6371
if 'consumer_secret' in kwargs:
64-
cls.consumer_secret = kwargs['consumer_secret']
72+
instance.consumer_secret = kwargs['consumer_secret']
6573

6674
if 'access_token' in kwargs:
67-
cls.access_token = kwargs['access_token']
75+
instance.access_token = kwargs['access_token']
6876

6977
if 'access_token_secret' in kwargs:
70-
cls.access_token_secret = kwargs['access_token_secret']
78+
instance.access_token_secret = kwargs['access_token_secret']
7179

7280
if 'company_id' in kwargs:
73-
cls.company_id = kwargs['company_id']
81+
instance.company_id = kwargs['company_id']
7482

7583
if 'callback_url' in kwargs:
76-
cls.callback_url = kwargs['callback_url']
84+
instance.callback_url = kwargs['callback_url']
7785

7886
if 'sandbox' in kwargs:
79-
cls.sandbox = kwargs['sandbox']
87+
instance.sandbox = kwargs['sandbox']
8088

8189
if 'minorversion' in kwargs:
82-
cls.minorversion = kwargs['minorversion']
90+
instance.minorversion = kwargs['minorversion']
8391

84-
return QuickBooks.__instance
92+
return instance
8593

8694
@classmethod
8795
def get_instance(cls):
8896
return cls.__instance
8997

98+
@classmethod
99+
def disable_global(cls):
100+
"""
101+
Disable use of singleton pattern.
102+
"""
103+
QuickBooks.__use_global = False
104+
QuickBooks.__instance = None
105+
106+
@classmethod
107+
def enable_global(cls):
108+
"""
109+
Allow use of singleton pattern.
110+
"""
111+
QuickBooks.__use_global = True
112+
90113
def _drop(self):
91114
QuickBooks.__instance = None
92115

@@ -157,7 +180,6 @@ def get_access_tokens(self, oauth_verifier):
157180
return session
158181

159182
def make_request(self, request_type, url, request_body=None, content_type='application/json'):
160-
161183
params = {}
162184

163185
if self.minorversion:
@@ -175,14 +197,19 @@ def make_request(self, request_type, url, request_body=None, content_type='appli
175197
}
176198

177199
req = self.session.request(request_type, url, True, self.company_id, headers=headers, params=params, data=request_body)
200+
if req.status_code == httplib.UNAUTHORIZED:
201+
raise AuthorizationException("Application authentication failed", detail=req.text)
178202

179203
try:
180204
result = req.json()
181205
except:
182206
raise QuickbooksException("Error reading json response: {0}".format(req.text), 10000)
183207

184-
if req.status_code is not httplib.OK or "Fault" in result:
208+
if "Fault" in result:
185209
self.handle_exceptions(result["Fault"])
210+
elif not req.status_code == httplib.OK:
211+
raise QuickbooksException("Error returned with status code '{0}': {1}".format(
212+
req.status_code, req.text), 10000)
186213
else:
187214
return result
188215

quickbooks/exceptions.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ class AuthorizationException(QuickbooksException):
1313
"""
1414
Quickbooks Error Codes from 1 to 499
1515
"""
16-
pass
16+
def __str__(self):
17+
return "QB Auth Exception: " + self.message + " \n\n" + self.detail
1718

1819

1920
class UnsupportedException(QuickbooksException):

0 commit comments

Comments
 (0)