1
1
from urllib .parse import quote
2
+ import decimal
2
3
3
- try : import simplejson as json
4
- except ImportError : import json
4
+ try :
5
+ import simplejson as json
6
+ except ImportError :
7
+ import json
5
8
6
9
from .utils import build_where_clause , build_choose_clause
7
10
from .client import QuickBooks
8
11
from .exceptions import QuickbooksException
9
12
10
13
14
+ class DecimalEncoder (json .JSONEncoder ):
15
+ def default (self , o ):
16
+ if isinstance (o , decimal .Decimal ):
17
+ return str (o )
18
+ return super (DecimalEncoder , self ).default (o )
19
+
20
+
11
21
class ToJsonMixin (object ):
12
22
def to_json (self ):
13
- return json .dumps (self , default = self .json_filter (), sort_keys = True , indent = 4 )
23
+ return json .dumps (
24
+ self ,
25
+ cls = DecimalEncoder ,
26
+ default = self .json_filter (),
27
+ sort_keys = True ,
28
+ indent = 4 ,
29
+ )
14
30
15
31
def json_filter (self ):
16
32
"""
17
33
filter out properties that have names starting with _
18
34
or properties that have a value of None
19
35
"""
20
- return lambda obj : dict ((k , v ) for k , v in obj .__dict__ .items ()
21
- if not k .startswith ('_' ) and getattr (obj , k ) is not None )
36
+ return lambda obj : (
37
+ str (obj )
38
+ if isinstance (obj , decimal .Decimal )
39
+ else dict (
40
+ (k , v )
41
+ for k , v in obj .__dict__ .items ()
42
+ if not k .startswith ("_" ) and getattr (obj , k ) is not None
43
+ )
44
+ )
22
45
23
46
24
47
class FromJsonMixin (object ):
@@ -39,8 +62,8 @@ def from_json(cls, json_data):
39
62
40
63
for data in json_data [key ]:
41
64
42
- if ' DetailType' in data and data [' DetailType' ] in obj .detail_dict :
43
- sub_obj = obj .detail_dict [data [' DetailType' ]]()
65
+ if " DetailType" in data and data [" DetailType" ] in obj .detail_dict :
66
+ sub_obj = obj .detail_dict [data [" DetailType" ]]()
44
67
else :
45
68
sub_obj = obj .list_dict [key ]()
46
69
@@ -61,17 +84,21 @@ def to_dict(obj, classkey=None):
61
84
"""
62
85
if isinstance (obj , dict ):
63
86
data = {}
64
- for ( k , v ) in obj .items ():
87
+ for k , v in obj .items ():
65
88
data [k ] = to_dict (v , classkey )
66
89
return data
67
90
elif hasattr (obj , "_ast" ):
68
91
return to_dict (obj ._ast ())
69
92
elif hasattr (obj , "__iter__" ) and not isinstance (obj , str ):
70
93
return [to_dict (v , classkey ) for v in obj ]
71
94
elif hasattr (obj , "__dict__" ):
72
- data = dict ([(key , to_dict (value , classkey ))
73
- for key , value in obj .__dict__ .items ()
74
- if not callable (value ) and not key .startswith ('_' )])
95
+ data = dict (
96
+ [
97
+ (key , to_dict (value , classkey ))
98
+ for key , value in obj .__dict__ .items ()
99
+ if not callable (value ) and not key .startswith ("_" )
100
+ ]
101
+ )
75
102
76
103
if classkey is not None and hasattr (obj , "__class__" ):
77
104
data [classkey ] = obj .__class__ .__name__
@@ -96,7 +123,7 @@ def get(cls, id, qb=None):
96
123
97
124
json_data = qb .get_single_object (cls .qbo_object_name , pk = id )
98
125
99
- if cls .qbo_json_object_name != '' :
126
+ if cls .qbo_json_object_name != "" :
100
127
return cls .from_json (json_data [cls .qbo_json_object_name ])
101
128
else :
102
129
return cls .from_json (json_data [cls .qbo_object_name ])
@@ -110,10 +137,10 @@ def send(self, qb=None, send_to=None):
110
137
end_point = "{0}/{1}/send" .format (self .qbo_object_name .lower (), self .Id )
111
138
112
139
if send_to :
113
- send_to = quote (send_to , safe = '' )
140
+ send_to = quote (send_to , safe = "" )
114
141
end_point = "{0}?sendTo={1}" .format (end_point , send_to )
115
142
116
- results = qb .misc_operation (end_point , None , ' application/octet-stream' )
143
+ results = qb .misc_operation (end_point , None , " application/octet-stream" )
117
144
118
145
return results
119
146
@@ -122,18 +149,9 @@ class VoidMixin(object):
122
149
123
150
def get_void_params (self ):
124
151
qb_object_params_map = {
125
- "Payment" : {
126
- "operation" : "update" ,
127
- "include" : "void"
128
- },
129
- "SalesReceipt" : {
130
- "operation" : "update" ,
131
- "include" : "void"
132
- },
133
- "BillPayment" : {
134
- "operation" : "update" ,
135
- "include" : "void"
136
- },
152
+ "Payment" : {"operation" : "update" , "include" : "void" },
153
+ "SalesReceipt" : {"operation" : "update" , "include" : "void" },
154
+ "BillPayment" : {"operation" : "update" , "include" : "void" },
137
155
"Invoice" : {
138
156
"operation" : "void" ,
139
157
},
@@ -143,21 +161,13 @@ def get_void_params(self):
143
161
144
162
def get_void_data (self ):
145
163
qb_object_params_map = {
146
- "Payment" : {
147
- "Id" : self .Id ,
148
- "SyncToken" : self .SyncToken ,
149
- "sparse" : True
150
- },
164
+ "Payment" : {"Id" : self .Id , "SyncToken" : self .SyncToken , "sparse" : True },
151
165
"SalesReceipt" : {
152
166
"Id" : self .Id ,
153
167
"SyncToken" : self .SyncToken ,
154
- "sparse" : True
155
- },
156
- "BillPayment" : {
157
- "Id" : self .Id ,
158
- "SyncToken" : self .SyncToken ,
159
- "sparse" : True
168
+ "sparse" : True ,
160
169
},
170
+ "BillPayment" : {"Id" : self .Id , "SyncToken" : self .SyncToken , "sparse" : True },
161
171
"Invoice" : {
162
172
"Id" : self .Id ,
163
173
"SyncToken" : self .SyncToken ,
@@ -171,7 +181,7 @@ def void(self, qb=None):
171
181
qb = QuickBooks ()
172
182
173
183
if not self .Id :
174
- raise QuickbooksException (' Cannot void unsaved object' )
184
+ raise QuickbooksException (" Cannot void unsaved object" )
175
185
176
186
endpoint = self .qbo_object_name .lower ()
177
187
url = "{0}/company/{1}/{2}" .format (qb .api_url , qb .company_id , endpoint )
@@ -192,11 +202,21 @@ def save(self, qb=None, request_id=None, params=None):
192
202
qb = QuickBooks ()
193
203
194
204
if self .Id and int (self .Id ) > 0 :
195
- json_data = qb .update_object (self .qbo_object_name , self .to_json (), request_id = request_id , params = params )
205
+ json_data = qb .update_object (
206
+ self .qbo_object_name ,
207
+ self .to_json (),
208
+ request_id = request_id ,
209
+ params = params ,
210
+ )
196
211
else :
197
- json_data = qb .create_object (self .qbo_object_name , self .to_json (), request_id = request_id , params = params )
198
-
199
- if self .qbo_json_object_name != '' :
212
+ json_data = qb .create_object (
213
+ self .qbo_object_name ,
214
+ self .to_json (),
215
+ request_id = request_id ,
216
+ params = params ,
217
+ )
218
+
219
+ if self .qbo_json_object_name != "" :
200
220
obj = type (self ).from_json (json_data [self .qbo_json_object_name ])
201
221
else :
202
222
obj = type (self ).from_json (json_data [self .qbo_object_name ])
@@ -213,7 +233,9 @@ def save(self, qb=None, request_id=None):
213
233
if not qb :
214
234
qb = QuickBooks ()
215
235
216
- json_data = qb .update_object (self .qbo_object_name , self .to_json (), request_id = request_id )
236
+ json_data = qb .update_object (
237
+ self .qbo_object_name , self .to_json (), request_id = request_id
238
+ )
217
239
obj = type (self ).from_json (json_data [self .qbo_object_name ])
218
240
return obj
219
241
@@ -226,13 +248,15 @@ def delete(self, qb=None, request_id=None):
226
248
qb = QuickBooks ()
227
249
228
250
if not self .Id :
229
- raise QuickbooksException (' Cannot delete unsaved object' )
251
+ raise QuickbooksException (" Cannot delete unsaved object" )
230
252
231
253
data = {
232
- 'Id' : self .Id ,
233
- ' SyncToken' : self .SyncToken ,
254
+ "Id" : self .Id ,
255
+ " SyncToken" : self .SyncToken ,
234
256
}
235
- return qb .delete_object (self .qbo_object_name , json .dumps (data ), request_id = request_id )
257
+ return qb .delete_object (
258
+ self .qbo_object_name , json .dumps (data ), request_id = request_id
259
+ )
236
260
237
261
238
262
class DeleteNoIdMixin (object ):
@@ -242,7 +266,9 @@ def delete(self, qb=None, request_id=None):
242
266
if not qb :
243
267
qb = QuickBooks ()
244
268
245
- return qb .delete_object (self .qbo_object_name , self .to_json (), request_id = request_id )
269
+ return qb .delete_object (
270
+ self .qbo_object_name , self .to_json (), request_id = request_id
271
+ )
246
272
247
273
248
274
class ListMixin (object ):
@@ -257,8 +283,13 @@ def all(cls, order_by="", start_position="", max_results=100, qb=None):
257
283
:param qb:
258
284
:return: Returns list
259
285
"""
260
- return cls .where ("" , order_by = order_by , start_position = start_position ,
261
- max_results = max_results , qb = qb )
286
+ return cls .where (
287
+ "" ,
288
+ order_by = order_by ,
289
+ start_position = start_position ,
290
+ max_results = max_results ,
291
+ qb = qb ,
292
+ )
262
293
263
294
@classmethod
264
295
def filter (cls , order_by = "" , start_position = "" , max_results = "" , qb = None , ** kwargs ):
@@ -270,9 +301,13 @@ def filter(cls, order_by="", start_position="", max_results="", qb=None, **kwarg
270
301
:param kwargs: field names and values to filter the query
271
302
:return: Filtered list
272
303
"""
273
- return cls .where (build_where_clause (** kwargs ),
274
- start_position = start_position , max_results = max_results , order_by = order_by ,
275
- qb = qb )
304
+ return cls .where (
305
+ build_where_clause (** kwargs ),
306
+ start_position = start_position ,
307
+ max_results = max_results ,
308
+ order_by = order_by ,
309
+ qb = qb ,
310
+ )
276
311
277
312
@classmethod
278
313
def choose (cls , choices , field = "Id" , qb = None ):
@@ -285,7 +320,9 @@ def choose(cls, choices, field="Id", qb=None):
285
320
return cls .where (build_choose_clause (choices , field ), qb = qb )
286
321
287
322
@classmethod
288
- def where (cls , where_clause = "" , order_by = "" , start_position = "" , max_results = "" , qb = None ):
323
+ def where (
324
+ cls , where_clause = "" , order_by = "" , start_position = "" , max_results = "" , qb = None
325
+ ):
289
326
"""
290
327
:param where_clause: QBO SQL where clause (DO NOT include 'WHERE')
291
328
:param order_by:
@@ -307,7 +344,8 @@ def where(cls, where_clause="", order_by="", start_position="", max_results="",
307
344
max_results = " MAXRESULTS " + str (max_results )
308
345
309
346
select = "SELECT * FROM {0} {1}{2}{3}{4}" .format (
310
- cls .qbo_object_name , where_clause , order_by , start_position , max_results )
347
+ cls .qbo_object_name , where_clause , order_by , start_position , max_results
348
+ )
311
349
312
350
return cls .query (select , qb = qb )
313
351
@@ -325,7 +363,7 @@ def query(cls, select, qb=None):
325
363
326
364
obj_list = []
327
365
328
- if cls .qbo_json_object_name != '' :
366
+ if cls .qbo_json_object_name != "" :
329
367
object_name = cls .qbo_json_object_name
330
368
else :
331
369
object_name = cls .qbo_object_name
@@ -350,7 +388,8 @@ def count(cls, where_clause="", qb=None):
350
388
where_clause = "WHERE " + where_clause
351
389
352
390
select = "SELECT COUNT(*) FROM {0} {1}" .format (
353
- cls .qbo_object_name , where_clause )
391
+ cls .qbo_object_name , where_clause
392
+ )
354
393
355
394
json_data = qb .query (select )
356
395
@@ -369,7 +408,9 @@ def download_pdf(self, qb=None):
369
408
else :
370
409
raise QuickbooksException (
371
410
"Cannot download {0} when no Id is assigned or if no quickbooks client is passed in" .format (
372
- self .qbo_object_name ))
411
+ self .qbo_object_name
412
+ )
413
+ )
373
414
374
415
375
416
class ObjectListMixin (object ):
0 commit comments