11from urllib .parse import quote
2+ import decimal
23
3- try : import simplejson as json
4- except ImportError : import json
4+ try :
5+ import simplejson as json
6+ except ImportError :
7+ import json
58
69from .utils import build_where_clause , build_choose_clause
710from .client import QuickBooks
811from .exceptions import QuickbooksException
912
1013
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+
1121class ToJsonMixin (object ):
1222 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+ )
1430
1531 def json_filter (self ):
1632 """
1733 filter out properties that have names starting with _
1834 or properties that have a value of None
1935 """
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+ )
2245
2346
2447class FromJsonMixin (object ):
@@ -39,8 +62,8 @@ def from_json(cls, json_data):
3962
4063 for data in json_data [key ]:
4164
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" ]]()
4467 else :
4568 sub_obj = obj .list_dict [key ]()
4669
@@ -61,17 +84,21 @@ def to_dict(obj, classkey=None):
6184 """
6285 if isinstance (obj , dict ):
6386 data = {}
64- for ( k , v ) in obj .items ():
87+ for k , v in obj .items ():
6588 data [k ] = to_dict (v , classkey )
6689 return data
6790 elif hasattr (obj , "_ast" ):
6891 return to_dict (obj ._ast ())
6992 elif hasattr (obj , "__iter__" ) and not isinstance (obj , str ):
7093 return [to_dict (v , classkey ) for v in obj ]
7194 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+ )
75102
76103 if classkey is not None and hasattr (obj , "__class__" ):
77104 data [classkey ] = obj .__class__ .__name__
@@ -96,7 +123,7 @@ def get(cls, id, qb=None):
96123
97124 json_data = qb .get_single_object (cls .qbo_object_name , pk = id )
98125
99- if cls .qbo_json_object_name != '' :
126+ if cls .qbo_json_object_name != "" :
100127 return cls .from_json (json_data [cls .qbo_json_object_name ])
101128 else :
102129 return cls .from_json (json_data [cls .qbo_object_name ])
@@ -110,10 +137,10 @@ def send(self, qb=None, send_to=None):
110137 end_point = "{0}/{1}/send" .format (self .qbo_object_name .lower (), self .Id )
111138
112139 if send_to :
113- send_to = quote (send_to , safe = '' )
140+ send_to = quote (send_to , safe = "" )
114141 end_point = "{0}?sendTo={1}" .format (end_point , send_to )
115142
116- results = qb .misc_operation (end_point , None , ' application/octet-stream' )
143+ results = qb .misc_operation (end_point , None , " application/octet-stream" )
117144
118145 return results
119146
@@ -122,18 +149,9 @@ class VoidMixin(object):
122149
123150 def get_void_params (self ):
124151 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" },
137155 "Invoice" : {
138156 "operation" : "void" ,
139157 },
@@ -143,21 +161,13 @@ def get_void_params(self):
143161
144162 def get_void_data (self ):
145163 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 },
151165 "SalesReceipt" : {
152166 "Id" : self .Id ,
153167 "SyncToken" : self .SyncToken ,
154- "sparse" : True
155- },
156- "BillPayment" : {
157- "Id" : self .Id ,
158- "SyncToken" : self .SyncToken ,
159- "sparse" : True
168+ "sparse" : True ,
160169 },
170+ "BillPayment" : {"Id" : self .Id , "SyncToken" : self .SyncToken , "sparse" : True },
161171 "Invoice" : {
162172 "Id" : self .Id ,
163173 "SyncToken" : self .SyncToken ,
@@ -171,7 +181,7 @@ def void(self, qb=None):
171181 qb = QuickBooks ()
172182
173183 if not self .Id :
174- raise QuickbooksException (' Cannot void unsaved object' )
184+ raise QuickbooksException (" Cannot void unsaved object" )
175185
176186 endpoint = self .qbo_object_name .lower ()
177187 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):
192202 qb = QuickBooks ()
193203
194204 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+ )
196211 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 != "" :
200220 obj = type (self ).from_json (json_data [self .qbo_json_object_name ])
201221 else :
202222 obj = type (self ).from_json (json_data [self .qbo_object_name ])
@@ -213,7 +233,9 @@ def save(self, qb=None, request_id=None):
213233 if not qb :
214234 qb = QuickBooks ()
215235
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+ )
217239 obj = type (self ).from_json (json_data [self .qbo_object_name ])
218240 return obj
219241
@@ -226,13 +248,15 @@ def delete(self, qb=None, request_id=None):
226248 qb = QuickBooks ()
227249
228250 if not self .Id :
229- raise QuickbooksException (' Cannot delete unsaved object' )
251+ raise QuickbooksException (" Cannot delete unsaved object" )
230252
231253 data = {
232- 'Id' : self .Id ,
233- ' SyncToken' : self .SyncToken ,
254+ "Id" : self .Id ,
255+ " SyncToken" : self .SyncToken ,
234256 }
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+ )
236260
237261
238262class DeleteNoIdMixin (object ):
@@ -242,7 +266,9 @@ def delete(self, qb=None, request_id=None):
242266 if not qb :
243267 qb = QuickBooks ()
244268
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+ )
246272
247273
248274class ListMixin (object ):
@@ -257,8 +283,13 @@ def all(cls, order_by="", start_position="", max_results=100, qb=None):
257283 :param qb:
258284 :return: Returns list
259285 """
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+ )
262293
263294 @classmethod
264295 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
270301 :param kwargs: field names and values to filter the query
271302 :return: Filtered list
272303 """
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+ )
276311
277312 @classmethod
278313 def choose (cls , choices , field = "Id" , qb = None ):
@@ -285,7 +320,9 @@ def choose(cls, choices, field="Id", qb=None):
285320 return cls .where (build_choose_clause (choices , field ), qb = qb )
286321
287322 @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+ ):
289326 """
290327 :param where_clause: QBO SQL where clause (DO NOT include 'WHERE')
291328 :param order_by:
@@ -307,7 +344,8 @@ def where(cls, where_clause="", order_by="", start_position="", max_results="",
307344 max_results = " MAXRESULTS " + str (max_results )
308345
309346 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+ )
311349
312350 return cls .query (select , qb = qb )
313351
@@ -325,7 +363,7 @@ def query(cls, select, qb=None):
325363
326364 obj_list = []
327365
328- if cls .qbo_json_object_name != '' :
366+ if cls .qbo_json_object_name != "" :
329367 object_name = cls .qbo_json_object_name
330368 else :
331369 object_name = cls .qbo_object_name
@@ -350,7 +388,8 @@ def count(cls, where_clause="", qb=None):
350388 where_clause = "WHERE " + where_clause
351389
352390 select = "SELECT COUNT(*) FROM {0} {1}" .format (
353- cls .qbo_object_name , where_clause )
391+ cls .qbo_object_name , where_clause
392+ )
354393
355394 json_data = qb .query (select )
356395
@@ -369,7 +408,9 @@ def download_pdf(self, qb=None):
369408 else :
370409 raise QuickbooksException (
371410 "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+ )
373414
374415
375416class ObjectListMixin (object ):
0 commit comments