Skip to content

Commit ea194e8

Browse files
authored
Merge pull request #356 from laf-rge/master
Convert to Using Decimal and Fix Related Test
2 parents cea08b2 + 031831e commit ea194e8

File tree

8 files changed

+220
-293
lines changed

8 files changed

+220
-293
lines changed

Pipfile

-1
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,4 @@ pytest-cov = "*"
1313
urllib3 = ">=2.1.0"
1414
intuit-oauth = "==1.2.5"
1515
requests = ">=2.31.0"
16-
simplejson = ">=3.19.1"
1716
requests_oauthlib = ">=1.3.1"

Pipfile.lock

+181-276
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

quickbooks/client.py

+11-2
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
import base64
55
import hashlib
66
import hmac
7+
import decimal
78

89
from . import exceptions
910
from requests_oauthlib import OAuth2Session
1011

11-
to_bytes = lambda value, *args, **kwargs: bytes(value, "utf-8", *args, **kwargs)
12+
def to_bytes(value, *args, **kwargs):
13+
return bytes(value, "utf-8", *args, **kwargs)
1214

1315

1416
class Environments(object):
@@ -24,6 +26,7 @@ class QuickBooks(object):
2426
minorversion = None
2527
verifier_token = None
2628
invoice_link = False
29+
use_decimal = False
2730

2831
sandbox_api_url_v3 = "https://sandbox-quickbooks.api.intuit.com/v3"
2932
api_url_v3 = "https://quickbooks.api.intuit.com/v3"
@@ -79,6 +82,9 @@ def __new__(cls, **kwargs):
7982
if 'verifier_token' in kwargs:
8083
instance.verifier_token = kwargs.get('verifier_token')
8184

85+
if 'use_decimal' in kwargs:
86+
instance.use_decimal = kwargs.get('use_decimal')
87+
8288
return instance
8389

8490
def _start_session(self):
@@ -206,7 +212,10 @@ def make_request(self, request_type, url, request_body=None, content_type='appli
206212
"Application authentication failed", error_code=req.status_code, detail=req.text)
207213

208214
try:
209-
result = req.json()
215+
if (self.use_decimal):
216+
result = json.loads(req.text, parse_float=decimal.Decimal)
217+
else:
218+
result = json.loads(req.text)
210219
except:
211220
raise exceptions.QuickbooksException("Error reading json response: {0}".format(req.text), 10000)
212221

quickbooks/mixins.py

+11-7
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
1+
import decimal
2+
import json
13
from urllib.parse import quote
24

3-
try: import simplejson as json
4-
except ImportError: import json
5-
6-
from .utils import build_where_clause, build_choose_clause
75
from .client import QuickBooks
86
from .exceptions import QuickbooksException
7+
from .utils import build_choose_clause, build_where_clause
98

9+
class DecimalEncoder(json.JSONEncoder):
10+
def default(self, obj):
11+
if isinstance(obj, decimal.Decimal):
12+
return str(obj)
13+
return super(DecimalEncoder, self).default(obj)
1014

1115
class ToJsonMixin(object):
1216
def to_json(self):
13-
return json.dumps(self, default=self.json_filter(), sort_keys=True, indent=4)
17+
return json.dumps(self, cls=DecimalEncoder, default=self.json_filter(), sort_keys=True, indent=4)
1418

1519
def json_filter(self):
1620
"""
@@ -178,7 +182,7 @@ def void(self, qb=None):
178182

179183
data = self.get_void_data()
180184
params = self.get_void_params()
181-
results = qb.post(url, json.dumps(data), params=params)
185+
results = qb.post(url, json.dumps(data, cls=DecimalEncoder), params=params)
182186

183187
return results
184188

@@ -232,7 +236,7 @@ def delete(self, qb=None, request_id=None):
232236
'Id': self.Id,
233237
'SyncToken': self.SyncToken,
234238
}
235-
return qb.delete_object(self.qbo_object_name, json.dumps(data), request_id=request_id)
239+
return qb.delete_object(self.qbo_object_name, json.dumps(data, cls=DecimalEncoder), request_id=request_id)
236240

237241

238242
class DeleteNoIdMixin(object):

quickbooks/objects/payment.py

-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
from ..client import QuickBooks
55
from .creditcardpayment import CreditCardPayment
66
from ..mixins import DeleteMixin, VoidMixin
7-
import json
87

98

109
class PaymentLine(QuickbooksBaseObject):

requirements.txt

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
intuit-oauth==1.2.4
22
requests_oauthlib>=1.3.1
3-
requests>=2.31.0
4-
simplejson>=3.19.1
3+
requests>=2.31.0

setup.py

-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ def read(*parts):
3434
'intuit-oauth==1.2.5',
3535
'requests_oauthlib>=1.3.1',
3636
'requests>=2.31.0',
37-
'simplejson>=3.19.1',
3837
'python-dateutil',
3938
],
4039

tests/unit/test_client.py

+16-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import json
12
from tests.integration.test_base import QuickbooksUnitTestCase
23

34
try:
@@ -6,7 +7,7 @@
67
from unittest.mock import patch, mock_open
78

89
from quickbooks.exceptions import QuickbooksException, SevereException, AuthorizationException
9-
from quickbooks import client
10+
from quickbooks import client, mixins
1011
from quickbooks.objects.salesreceipt import SalesReceipt
1112

1213

@@ -141,7 +142,7 @@ def test_get_single_object(self, make_req):
141142

142143
@patch('quickbooks.client.QuickBooks.process_request')
143144
def test_make_request(self, process_request):
144-
process_request.return_value = MockResponse()
145+
process_request.return_value = MockResponseJson()
145146

146147
qb_client = client.QuickBooks()
147148
qb_client.company_id = "1234"
@@ -220,7 +221,7 @@ def test_download_pdf_not_authorized(self, process_request):
220221
@patch('quickbooks.client.QuickBooks.process_request')
221222
def test_make_request_file_closed(self, process_request):
222223
file_path = '/path/to/file.txt'
223-
process_request.return_value = MockResponse()
224+
process_request.return_value = MockResponseJson()
224225
with patch('builtins.open', mock_open(read_data=b'file content')) as mock_file:
225226
qb_client = client.QuickBooks(auth_client=self.auth_client)
226227
qb_client.make_request('POST',
@@ -253,6 +254,18 @@ def json(self):
253254
def content(self):
254255
return ''
255256

257+
class MockResponseJson:
258+
def __init__(self, json_data=None, status_code=200):
259+
self.json_data = json_data or {}
260+
self.status_code = status_code
261+
262+
@property
263+
def text(self):
264+
return json.dumps(self.json_data, cls=mixins.DecimalEncoder)
265+
266+
def json(self):
267+
return self.json_data
268+
256269

257270
class MockUnauthorizedResponse(object):
258271
@property

0 commit comments

Comments
 (0)