Skip to content

Commit 04d5563

Browse files
authored
Merge branch '0.9.12' into minor-version
2 parents b4014b4 + 5efc535 commit 04d5563

20 files changed

+224
-86
lines changed

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
publish: clean
2-
python setup.py sdist
2+
python -m build
33
twine upload dist/*
44

55
clean:
66
rm -vrf ./build ./dist ./*.egg-info
77
find . -name '*.pyc' -delete
8-
find . -name '*.tgz' -delete
8+
find . -name '*.tgz' -delete

Pipfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@ urllib3 = ">=2.1.0"
1414
intuit-oauth = "==1.2.6"
1515
requests = ">=2.31.0"
1616
requests_oauthlib = ">=1.3.1"
17-
setuptools = "*"
17+
build = "*"

Pipfile.lock

Lines changed: 26 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ A Python 3 library for accessing the Quickbooks API. Complete rework of
1010
[quickbooks-python](https://github.com/troolee/quickbooks-python).
1111

1212
These instructions were written for a Django application. Make sure to
13-
change it to whatever framework/method youre using.
13+
change it to whatever framework/method you're using.
1414
You can find additional examples of usage in [Integration tests folder](https://github.com/ej2/python-quickbooks/tree/master/tests/integration).
1515

1616
For information about contributing, see the [Contributing Page](https://github.com/ej2/python-quickbooks/blob/master/contributing.md).
@@ -247,6 +247,22 @@ Attaching a file to customer:
247247
attachment.ContentType = 'application/pdf'
248248
attachment.save(qb=client)
249249

250+
Attaching file bytes to customer:
251+
252+
attachment = Attachable()
253+
254+
attachable_ref = AttachableRef()
255+
attachable_ref.EntityRef = customer.to_ref()
256+
257+
attachment.AttachableRef.append(attachable_ref)
258+
259+
attachment.FileName = 'Filename'
260+
attachment._FileBytes = pdf_bytes # bytes object containing the file content
261+
attachment.ContentType = 'application/pdf'
262+
attachment.save(qb=client)
263+
264+
**Note:** You can use either `_FilePath` or `_FileBytes` to attach a file, but not both at the same time.
265+
250266
Passing in optional params
251267
----------------
252268
Some QBO objects have options that need to be set on the query string of an API call.

dev_requirements.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
coverage==7.3.0
22
ipdb==0.13.13
3-
mock==5.1.0
43
nose==1.3.7

quickbooks/client.py

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ def change_data_capture(self, entity_string, changed_since):
157157
return result
158158

159159
def make_request(self, request_type, url, request_body=None, content_type='application/json',
160-
params=None, file_path=None, request_id=None):
160+
params=None, file_path=None, file_bytes=None, request_id=None):
161161

162162
if not params:
163163
params = {}
@@ -176,7 +176,7 @@ def make_request(self, request_type, url, request_body=None, content_type='appli
176176
'User-Agent': 'python-quickbooks V3 library'
177177
}
178178

179-
if file_path:
179+
if file_path or file_bytes:
180180
url = url.replace('attachable', 'upload')
181181
boundary = '-------------PythonMultipartPost'
182182
headers.update({
@@ -187,8 +187,11 @@ def make_request(self, request_type, url, request_body=None, content_type='appli
187187
'Connection': 'close'
188188
})
189189

190-
with open(file_path, 'rb') as attachment:
191-
binary_data = str(base64.b64encode(attachment.read()).decode('ascii'))
190+
if file_path:
191+
with open(file_path, 'rb') as attachment:
192+
binary_data = str(base64.b64encode(attachment.read()).decode('ascii'))
193+
else:
194+
binary_data = str(base64.b64encode(file_bytes).decode('ascii'))
192195

193196
content_type = json.loads(request_body)['ContentType']
194197

@@ -260,9 +263,10 @@ def process_request(self, request_type, url, headers="", params="", data=""):
260263
request_type, url, headers=headers, params=params, data=data)
261264

262265
def get_single_object(self, qbbo, pk, params=None):
263-
url = "{0}/company/{1}/{2}/{3}/".format(self.api_url, self.company_id, qbbo.lower(), pk)
266+
url = "{0}/company/{1}/{2}/{3}".format(self.api_url, self.company_id, qbbo.lower(), pk)
264267
if params is None:
265268
params = {}
269+
266270
return self.get(url, {}, params=params)
267271

268272
@staticmethod
@@ -299,11 +303,11 @@ def handle_exceptions(results):
299303
else:
300304
raise exceptions.QuickbooksException(message, code, detail)
301305

302-
def create_object(self, qbbo, request_body, _file_path=None, request_id=None, params=None):
306+
def create_object(self, qbbo, request_body, _file_path=None, _file_bytes=None, request_id=None, params=None):
303307
self.isvalid_object_name(qbbo)
304308

305309
url = "{0}/company/{1}/{2}".format(self.api_url, self.company_id, qbbo.lower())
306-
results = self.post(url, request_body, file_path=_file_path, request_id=request_id, params=params)
310+
results = self.post(url, request_body, file_path=_file_path, file_bytes=_file_bytes, request_id=request_id, params=params)
307311

308312
return results
309313

@@ -319,11 +323,14 @@ def isvalid_object_name(self, object_name):
319323

320324
return True
321325

322-
def update_object(self, qbbo, request_body, _file_path=None, request_id=None, params=None):
326+
def update_object(self, qbbo, request_body, _file_path=None, _file_bytes=None, request_id=None, params=None):
323327
url = "{0}/company/{1}/{2}".format(self.api_url, self.company_id, qbbo.lower())
324328
if params is None:
325329
params = {}
326-
return self.post(url, request_body, file_path=_file_path, request_id=request_id, params=params)
330+
331+
result = self.post(url, request_body, file_path=_file_path, file_bytes=_file_bytes, request_id=request_id, params=params)
332+
333+
return result
327334

328335
def delete_object(self, qbbo, request_body, _file_path=None, request_id=None):
329336
url = "{0}/company/{1}/{2}".format(self.api_url, self.company_id, qbbo.lower())

quickbooks/mixins.py

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -255,14 +255,26 @@ class ListMixin(object):
255255

256256
@classmethod
257257
def all(cls, order_by="", start_position="", max_results=100, qb=None):
258-
"""
259-
:param start_position:
260-
:param max_results: The max number of entities that can be returned in a response is 1000.
261-
:param qb:
262-
:return: Returns list
263-
"""
264-
return cls.where("", order_by=order_by, start_position=start_position,
265-
max_results=max_results, qb=qb)
258+
"""Returns list of objects containing all objects in the QuickBooks database"""
259+
if qb is None:
260+
qb = QuickBooks()
261+
262+
# For Item objects, we need to explicitly request the SKU field
263+
if cls.qbo_object_name == "Item":
264+
select = "SELECT *, Sku FROM {0}".format(cls.qbo_object_name)
265+
else:
266+
select = "SELECT * FROM {0}".format(cls.qbo_object_name)
267+
268+
if order_by:
269+
select += " ORDER BY {0}".format(order_by)
270+
271+
if start_position:
272+
select += " STARTPOSITION {0}".format(start_position)
273+
274+
if max_results:
275+
select += " MAXRESULTS {0}".format(max_results)
276+
277+
return cls.query(select, qb=qb)
266278

267279
@classmethod
268280
def filter(cls, order_by="", start_position="", max_results="", qb=None, **kwargs):

quickbooks/objects/attachable.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ def __init__(self):
2727
self.AttachableRef = []
2828
self.FileName = None
2929
self._FilePath = ''
30+
self._FileBytes = None
3031
self.Note = ""
3132
self.FileAccessUri = None
3233
self.TempDownloadUri = None
@@ -53,10 +54,18 @@ def save(self, qb=None):
5354
if not qb:
5455
qb = QuickBooks()
5556

57+
# Validate that we have either file path or bytes, but not both
58+
if self._FilePath and self._FileBytes:
59+
raise ValueError("Cannot specify both _FilePath and _FileBytes")
60+
5661
if self.Id and int(self.Id) > 0:
57-
json_data = qb.update_object(self.qbo_object_name, self.to_json(), _file_path=self._FilePath)
62+
json_data = qb.update_object(self.qbo_object_name, self.to_json(),
63+
_file_path=self._FilePath,
64+
_file_bytes=self._FileBytes)
5865
else:
59-
json_data = qb.create_object(self.qbo_object_name, self.to_json(), _file_path=self._FilePath)
66+
json_data = qb.create_object(self.qbo_object_name, self.to_json(),
67+
_file_path=self._FilePath,
68+
_file_bytes=self._FileBytes)
6069

6170
if self.Id is None and self.FileName:
6271
obj = type(self).from_json(json_data['AttachableResponse'][0]['Attachable'])

quickbooks/objects/timeactivity.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ def __init__(self):
3232
self.StartTime = None
3333
self.EndTime = None
3434
self.Description = None
35+
self.CostRate = None
3536

3637
self.VendorRef = None
3738
self.CustomerRef = None

setup.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ def read(*parts):
3030
},
3131

3232
install_requires=[
33-
'setuptools',
3433
'intuit-oauth==1.2.6',
3534
'requests_oauthlib>=1.3.1',
3635
'requests>=2.31.0',

0 commit comments

Comments
 (0)