Skip to content

Commit d1aec9e

Browse files
committed
Fixed tests and made misc updates.
2 parents 495208b + a576f41 commit d1aec9e

14 files changed

Lines changed: 244 additions & 57 deletions

File tree

.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.
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.

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 = True
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):

quickbooks/mixins.py

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,9 @@ class ReadMixin(object):
4747
qbo_object_name = ""
4848

4949
@classmethod
50-
def get(cls, id):
51-
qb = QuickBooks()
50+
def get(cls, id, qb=None):
51+
if not qb:
52+
qb = QuickBooks()
5253

5354
json_data = qb.get_single_object(cls.qbo_object_name, pk=id)
5455
return cls.from_json(json_data[cls.qbo_object_name])
@@ -57,8 +58,9 @@ def get(cls, id):
5758
class UpdateMixin(object):
5859
qbo_object_name = ""
5960

60-
def save(self):
61-
qb = QuickBooks()
61+
def save(self, qb=None):
62+
if not qb:
63+
qb = QuickBooks()
6264

6365
if self.Id and self.Id > 0:
6466
json_data = qb.update_object(self.qbo_object_name, self.to_json())
@@ -75,31 +77,31 @@ class ListMixin(object):
7577
qbo_object_name = ""
7678

7779
@classmethod
78-
def all(cls, start_position="", max_results=100):
80+
def all(cls, start_position="", max_results=100, qb=None):
7981
"""
8082
:param max_results: The maximum number of entities that can be returned in a response is 1000.
8183
:return: Returns list
8284
"""
83-
return cls.where("", start_position=start_position, max_results=max_results)
85+
return cls.where("", start_position=start_position, max_results=max_results, qb=qb)
8486

8587
@classmethod
86-
def filter(cls, start_position="", max_results="", **kwargs):
88+
def filter(cls, start_position="", max_results="", qb=None, **kwargs):
8789
"""
8890
:param kwargs: field names and values to filter the query
8991
:return: Filtered list
9092
"""
91-
return cls.where(build_where_clause(**kwargs), start_position=start_position, max_results=max_results)
93+
return cls.where(build_where_clause(**kwargs), start_position=start_position, max_results=max_results, qb=qb)
9294

9395
@classmethod
94-
def choose(cls, choices, field="Id"):
96+
def choose(cls, choices, field="Id", qb=None):
9597
"""
9698
:param kwargs: field names and values to filter the query
9799
:return: Filtered list
98100
"""
99-
return cls.where(build_choose_clause(choices, field))
101+
return cls.where(build_choose_clause(choices, field), qb=qb)
100102

101103
@classmethod
102-
def where(cls, where_clause="", start_position="", max_results=""):
104+
def where(cls, where_clause="", start_position="", max_results="", qb=None):
103105
"""
104106
:param where_clause: QBO SQL where clause (DO NOT include 'WHERE')
105107
:return: Returns list filtered by input where_clause
@@ -115,16 +117,16 @@ def where(cls, where_clause="", start_position="", max_results=""):
115117

116118
select = "SELECT * FROM {0} {1}{2}{3}".format(cls.qbo_object_name, where_clause, start_position, max_results)
117119

118-
return cls.query(select)
120+
return cls.query(select, qb=qb)
119121

120122
@classmethod
121-
def query(cls, select):
123+
def query(cls, select, qb=None):
122124
"""
123125
:param select: QBO SQL query select statement
124126
:return: Returns list
125127
"""
126-
127-
qb = QuickBooks()
128+
if not qb:
129+
qb = QuickBooks()
128130

129131
json_data = qb.query(select)
130132

quickbooks/objects/billpayment.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ def __init__(self):
8989
self.APAccountRef = None
9090
self.DepartmentRef = None
9191
self.CreditCardPayment = None
92+
self.CurrencyRef = None
9293

9394
self.Line = []
9495

quickbooks/objects/taxrate.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class TaxRate(QuickbooksManagedObject, QuickbooksTransactionEntity):
99
entity to create a taxrate.
1010
"""
1111
class_dict = {
12-
"AgencyRef": Ref
12+
"AgencyRef": Ref,
1313
}
1414

1515
qbo_object_name = "TaxRate"
@@ -21,11 +21,11 @@ def __init__(self):
2121
self.RateValue = 0
2222
self.SpecialTaxType = ""
2323
self.Active = True
24-
self.TaxReturnLineRef = ""
2524
self.DisplayType = ""
2625
self.EffectiveTaxRate = ""
2726

2827
self.AgencyRef = None
28+
self.TaxReturnLineRef = None
2929

3030
def __str__(self):
3131
return self.Name

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ def read(*parts):
1010
return fp.read()
1111

1212

13-
VERSION = (0, 3, 12)
13+
VERSION = (0, 3, 13)
1414
version = '.'.join(map(str, VERSION))
1515

1616
setup(

tests/integration/test_account.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
class AccountTest(unittest.TestCase):
99
def setUp(self):
10-
QuickBooks(
10+
self.qb_client = QuickBooks(
1111
sandbox=True,
1212
consumer_key=os.environ.get('CONSUMER_KEY'),
1313
consumer_secret=os.environ.get('CONSUMER_SECRET'),
@@ -24,10 +24,10 @@ def test_create(self):
2424
account.AcctNum = self.account_number
2525
account.Name = self.name
2626
account.AccountSubType = "CashOnHand"
27-
account.save()
27+
account.save(qb=self.qb_client)
2828

2929
self.id = account.Id
30-
query_account = Account.get(account.Id)
30+
query_account = Account.get(account.Id, qb=self.qb_client)
3131

3232
self.assertEquals(account.Id, query_account.Id)
3333
self.assertEquals(query_account.Name, self.name)
@@ -37,8 +37,8 @@ def test_update(self):
3737
account = Account.filter(Name=self.name)[0]
3838

3939
account.Name = "Updated Name {0}".format(self.account_number)
40-
account.save()
40+
account.save(qb=self.qb_client)
4141

42-
query_account = Account.get(account.Id)
42+
query_account = Account.get(account.Id, qb=self.qb_client)
4343

4444
self.assertEquals(query_account.Name, "Updated Name {0}".format(self.account_number))

0 commit comments

Comments
 (0)