Skip to content

Commit d2c811a

Browse files
committed
attachable bytes
1 parent 67141c2 commit d2c811a

File tree

4 files changed

+61
-11
lines changed

4 files changed

+61
-11
lines changed

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.

quickbooks/client.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ def change_data_capture(self, entity_string, changed_since):
152152
return result
153153

154154
def make_request(self, request_type, url, request_body=None, content_type='application/json',
155-
params=None, file_path=None, request_id=None):
155+
params=None, file_path=None, file_bytes=None, request_id=None):
156156

157157
if not params:
158158
params = {}
@@ -172,7 +172,7 @@ def make_request(self, request_type, url, request_body=None, content_type='appli
172172
'User-Agent': 'python-quickbooks V3 library'
173173
}
174174

175-
if file_path:
175+
if file_path or file_bytes:
176176
url = url.replace('attachable', 'upload')
177177
boundary = '-------------PythonMultipartPost'
178178
headers.update({
@@ -183,8 +183,11 @@ def make_request(self, request_type, url, request_body=None, content_type='appli
183183
'Connection': 'close'
184184
})
185185

186-
with open(file_path, 'rb') as attachment:
187-
binary_data = str(base64.b64encode(attachment.read()).decode('ascii'))
186+
if file_path:
187+
with open(file_path, 'rb') as attachment:
188+
binary_data = str(base64.b64encode(attachment.read()).decode('ascii'))
189+
else:
190+
binary_data = str(base64.b64encode(file_bytes).decode('ascii'))
188191

189192
content_type = json.loads(request_body)['ContentType']
190193

@@ -287,11 +290,11 @@ def handle_exceptions(results):
287290
else:
288291
raise exceptions.QuickbooksException(message, code, detail)
289292

290-
def create_object(self, qbbo, request_body, _file_path=None, request_id=None, params=None):
293+
def create_object(self, qbbo, request_body, _file_path=None, _file_bytes=None, request_id=None, params=None):
291294
self.isvalid_object_name(qbbo)
292295

293296
url = "{0}/company/{1}/{2}".format(self.api_url, self.company_id, qbbo.lower())
294-
results = self.post(url, request_body, file_path=_file_path, request_id=request_id, params=params)
297+
results = self.post(url, request_body, file_path=_file_path, file_bytes=_file_bytes, request_id=request_id, params=params)
295298

296299
return results
297300

@@ -307,9 +310,9 @@ def isvalid_object_name(self, object_name):
307310

308311
return True
309312

310-
def update_object(self, qbbo, request_body, _file_path=None, request_id=None, params=None):
313+
def update_object(self, qbbo, request_body, _file_path=None, _file_bytes=None, request_id=None, params=None):
311314
url = "{0}/company/{1}/{2}".format(self.api_url, self.company_id, qbbo.lower())
312-
result = self.post(url, request_body, file_path=_file_path, request_id=request_id, params=params)
315+
result = self.post(url, request_body, file_path=_file_path, file_bytes=_file_bytes, request_id=request_id, params=params)
313316

314317
return result
315318

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'])

tests/integration/test_attachable.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,26 @@ def test_create_file(self):
6868
self.assertEqual(query_attachable.AttachableRef[0].EntityRef.value, vendor.Id)
6969
self.assertEqual(query_attachable.Note, "Sample note")
7070

71+
def test_create_file_from_bytes(self):
72+
attachable = Attachable()
73+
file_content = b"File contents in bytes"
74+
75+
vendor = Vendor.all(max_results=1, qb=self.qb_client)[0]
76+
77+
attachable_ref = AttachableRef()
78+
attachable_ref.EntityRef = vendor.to_ref()
79+
attachable.AttachableRef.append(attachable_ref)
80+
81+
attachable.Note = "Sample note with bytes"
82+
attachable.FileName = "test.txt"
83+
attachable._FileBytes = file_content
84+
attachable.ContentType = 'text/plain'
85+
86+
attachable.save(qb=self.qb_client)
87+
88+
query_attachable = Attachable.get(attachable.Id, qb=self.qb_client)
89+
90+
self.assertEqual(query_attachable.AttachableRef[0].EntityRef.value, vendor.Id)
91+
self.assertEqual(query_attachable.Note, "Sample note with bytes")
92+
7193

0 commit comments

Comments
 (0)