GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
gnucash_rest.py
1 #!/usr/bin/python
2 
3 '''
4 
5 gnucash_rest.py -- A Flask app which responds to REST requests
6 with JSON responses
7 
8 Copyright (C) 2013 Tom Lofts <[email protected]>
9 
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU General Public License as
12 published by the Free Software Foundation; either version 2 of
13 the License, or (at your option) any later version.
14 
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, contact:
22 
23 Free Software Foundation Voice: +1-617-542-5942
24 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
25 Boston, MA 02110-1301, USA [email protected]
26 
27 @author Tom Lofts <[email protected]>
28 
29 '''
30 
31 import gnucash
32 import gnucash_simple
33 import json
34 import atexit
35 from flask import Flask, abort, request, Response
36 import sys
37 import getopt
38 
39 from decimal import Decimal
40 
41 from gnucash.gnucash_business import Vendor, Bill, Entry, GncNumeric, \
42  Customer, Invoice, Split, Account, Transaction
43 
44 import datetime
45 
46 from gnucash import \
47  QOF_QUERY_AND, \
48  QOF_QUERY_OR, \
49  QOF_QUERY_NAND, \
50  QOF_QUERY_NOR, \
51  QOF_QUERY_XOR
52 
53 from gnucash import \
54  QOF_STRING_MATCH_NORMAL, \
55  QOF_STRING_MATCH_CASEINSENSITIVE
56 
57 from gnucash import \
58  QOF_COMPARE_LT, \
59  QOF_COMPARE_LTE, \
60  QOF_COMPARE_EQUAL, \
61  QOF_COMPARE_GT, \
62  QOF_COMPARE_GTE, \
63  QOF_COMPARE_NEQ
64 
65 from gnucash import \
66  INVOICE_TYPE
67 
68 from gnucash import \
69  INVOICE_IS_PAID
70 
71 app = Flask(__name__)
72 app.debug = True
73 
74 @app.route('/accounts', methods=['GET', 'POST'])
75 def api_accounts():
76 
77  if request.method == 'GET':
78 
79  accounts = getAccounts(session.book)
80 
81  return Response(json.dumps(accounts), mimetype='application/json')
82 
83  elif request.method == 'POST':
84 
85  try:
86  account = addAccount(session.books)
87  except Error as error:
88  return Response(json.dumps({'errors': [{'type' : error.type,
89  'message': error.message, 'data': error.data}]}), status=400,
90  mimetype='application/json')
91  else:
92  return Response(json.dumps(account), status=201,
93  mimetype='application/json')
94 
95  else:
96  abort(405)
97 
98 @app.route('/accounts/<guid>', methods=['GET'])
99 def api_account(guid):
100 
101  account = getAccount(session.book, guid)
102 
103  if account is None:
104  abort(404)
105  else:
106  return Response(json.dumps(account), mimetype='application/json')
107 
108 @app.route('/accounts/<guid>/splits', methods=['GET'])
109 def api_account_splits(guid):
110 
111  date_posted_from = request.args.get('date_posted_from', None)
112  date_posted_to = request.args.get('date_posted_to', None)
113 
114  # check account exists
115  account = getAccount(session.book, guid)
116 
117  if account is None:
118  abort(404)
119 
120  splits = getAccountSplits(session.book, guid, date_posted_from,
121  date_posted_to)
122 
123  return Response(json.dumps(splits), mimetype='application/json')
124 
125 
126 @app.route('/transactions', methods=['POST'])
127 def api_transactions():
128 
129  if request.method == 'POST':
130 
131  currency = str(request.form.get('currency', ''))
132  description = str(request.form.get('description', ''))
133  num = str(request.form.get('num', ''))
134  date_posted = str(request.form.get('date_posted', ''))
135 
136  splitvalue1 = int(request.form.get('splitvalue1', ''))
137  splitaccount1 = str(request.form.get('splitaccount1', ''))
138  splitvalue2 = int(request.form.get('splitvalue2', ''))
139  splitaccount2 = str(request.form.get('splitaccount2', ''))
140 
141  splits = [
142  {'value': splitvalue1, 'account_guid': splitaccount1},
143  {'value': splitvalue2, 'account_guid': splitaccount2}]
144 
145  try:
146  transaction = addTransaction(session.book, num, description,
147  date_posted, currency, splits)
148  except Error as error:
149  return Response(json.dumps({'errors': [{'type' : error.type,
150  'message': error.message, 'data': error.data}]}), status=400,
151  mimetype='application/json')
152  else:
153  return Response(json.dumps(transaction), status=201,
154  mimetype='application/json')
155 
156  else:
157  abort(405)
158 
159 @app.route('/transactions/<guid>', methods=['GET', 'POST', 'DELETE'])
160 def api_transaction(guid):
161 
162  if request.method == 'GET':
163 
164  transaction = getTransaction(session.book, guid)
165 
166  if transaction is None:
167  abort(404)
168 
169  return Response(json.dumps(transaction), mimetype='application/json')
170 
171  elif request.method == 'POST':
172 
173  currency = str(request.form.get('currency', ''))
174  description = str(request.form.get('description', ''))
175  num = str(request.form.get('num', ''))
176  date_posted = str(request.form.get('date_posted', ''))
177 
178  splitguid1 = str(request.form.get('splitguid1', ''))
179  splitvalue1 = int(request.form.get('splitvalue1', ''))
180  splitaccount1 = str(request.form.get('splitaccount1', ''))
181  splitguid2 = str(request.form.get('splitguid2', ''))
182  splitvalue2 = int(request.form.get('splitvalue2', ''))
183  splitaccount2 = str(request.form.get('splitaccount2', ''))
184 
185  splits = [
186  {'guid': splitguid1,
187  'value': splitvalue1,
188  'account_guid': splitaccount1},
189  {'guid': splitguid2,
190  'value': splitvalue2,
191  'account_guid': splitaccount2}
192  ]
193 
194  try:
195  transaction = editTransaction(session.book, guid, num, description,
196  date_posted, currency, splits)
197  except Error as error:
198  return Response(json.dumps({'errors': [{'type' : error.type,
199  'message': error.message, 'data': error.data}]}), status=400, mimetype='application/json')
200  else:
201  return Response(json.dumps(transaction), status=200,
202  mimetype='application/json')
203 
204  elif request.method == 'DELETE':
205 
206  deleteTransaction(session.book, guid)
207 
208  return Response('', status=200, mimetype='application/json')
209 
210  else:
211  abort(405)
212 
213 @app.route('/bills', methods=['GET', 'POST'])
214 def api_bills():
215 
216  if request.method == 'GET':
217 
218  is_paid = request.args.get('is_paid', None)
219  is_active = request.args.get('is_active', None)
220  date_opened_to = request.args.get('date_opened_to', None)
221  date_opened_from = request.args.get('date_opened_from', None)
222 
223  if is_paid == '1':
224  is_paid = 1
225  elif is_paid == '0':
226  is_paid = 0
227  else:
228  is_paid = None
229 
230  if is_active == '1':
231  is_active = 1
232  elif is_active == '0':
233  is_active = 0
234  else:
235  is_active = None
236 
237  bills = getBills(session.book, None, is_paid, is_active,
238  date_opened_from, date_opened_to)
239 
240  return Response(json.dumps(bills), mimetype='application/json')
241 
242  elif request.method == 'POST':
243 
244  id = str(request.form.get('id', None))
245 
246  if id == '':
247  id = None
248  elif id != None:
249  id = str(id)
250 
251  vendor_id = str(request.form.get('vendor_id', ''))
252  currency = str(request.form.get('currency', ''))
253  date_opened = str(request.form.get('date_opened', ''))
254  notes = str(request.form.get('notes', ''))
255 
256  try:
257  bill = addBill(session.book, id, vendor_id, currency, date_opened,
258  notes)
259  except Error as error:
260  # handle incorrect parameter errors
261  return Response(json.dumps({'errors': [{'type' : error.type,
262  'message': error.message, 'data': error.data}]}), status=400, mimetype='application/json')
263  else:
264  return Response(json.dumps(bill), status=201,
265  mimetype='application/json')
266 
267  else:
268  abort(405)
269 
270 @app.route('/bills/<id>', methods=['GET', 'POST', 'PAY'])
271 def api_bill(id):
272 
273  if request.method == 'GET':
274 
275  bill = getBill(session.book, id)
276 
277  if bill is None:
278  abort(404)
279  else:
280  return Response(json.dumps(bill), mimetype='application/json')
281 
282  elif request.method == 'POST':
283 
284  vendor_id = str(request.form.get('vendor_id', ''))
285  currency = str(request.form.get('currency', ''))
286  date_opened = request.form.get('date_opened', None)
287  notes = str(request.form.get('notes', ''))
288  posted = request.form.get('posted', None)
289  posted_account_guid = str(request.form.get('posted_account_guid', ''))
290  posted_date = request.form.get('posted_date', '')
291  due_date = request.form.get('due_date', '')
292  posted_memo = str(request.form.get('posted_memo', ''))
293  posted_accumulatesplits = request.form.get('posted_accumulatesplits',
294  '')
295  posted_autopay = request.form.get('posted_autopay', '')
296 
297  if posted == '1':
298  posted = 1
299  else:
300  posted = 0
301 
302  if (posted_accumulatesplits == '1'
303  or posted_accumulatesplits == 'true'
304  or posted_accumulatesplits == 'True'
305  or posted_accumulatesplits == True):
306  posted_accumulatesplits = True
307  else:
308  posted_accumulatesplits = False
309 
310  if posted_autopay == '1':
311  posted_autopay = True
312  else:
313  posted_autopay = False
314  try:
315  bill = updateBill(session.book, id, vendor_id, currency,
316  date_opened, notes, posted, posted_account_guid, posted_date,
317  due_date, posted_memo, posted_accumulatesplits, posted_autopay)
318  except Error as error:
319  return Response(json.dumps({'errors': [{'type' : error.type,
320  'message': error.message, 'data': error.data}]}), status=400,
321  mimetype='application/json')
322  else:
323  return Response(json.dumps(bill), status=200,
324  mimetype='application/json')
325 
326  if bill is None:
327  abort(404)
328  else:
329  return Response(json.dumps(bill),
330  mimetype='application/json')
331 
332  elif request.method == 'PAY':
333 
334  posted_account_guid = str(request.form.get('posted_account_guid', ''))
335  transfer_account_guid = str(request.form.get('transfer_account_guid',
336  ''))
337  payment_date = request.form.get('payment_date', '')
338  num = str(request.form.get('num', ''))
339  memo = str(request.form.get('posted_memo', ''))
340  auto_pay = request.form.get('auto_pay', '')
341 
342  try:
343  bill = payBill(session.book, id, posted_account_guid,
344  transfer_account_guid, payment_date, memo, num, auto_pay)
345  except Error as error:
346  return Response(json.dumps({'errors': [{'type' : error.type,
347  'message': error.message, 'data': error.data}]}), status=400,
348  mimetype='application/json')
349  else:
350  return Response(json.dumps(bill), status=200,
351  mimetype='application/json')
352 
353  else:
354  abort(405)
355 
356 @app.route('/bills/<id>/entries', methods=['GET', 'POST'])
357 def api_bill_entries(id):
358 
359  bill = getBill(session.book, id)
360 
361  if bill is None:
362  abort(404)
363  else:
364  if request.method == 'GET':
365  return Response(json.dumps(bill['entries']), mimetype='application/json')
366  elif request.method == 'POST':
367 
368  date = str(request.form.get('date', ''))
369  description = str(request.form.get('description', ''))
370  account_guid = str(request.form.get('account_guid', ''))
371  quantity = str(request.form.get('quantity', ''))
372  price = str(request.form.get('price', ''))
373 
374  try:
375  entry = addBillEntry(session.book, id, date, description,
376  account_guid, quantity, price)
377  except Error as error:
378  return Response(json.dumps({'errors': [{'type' : error.type,
379  'message': error.message, 'data': error.data}]}),
380  status=400, mimetype='application/json')
381  else:
382  return Response(json.dumps(entry), status=201,
383  mimetype='application/json')
384 
385  else:
386  abort(405)
387 
388 @app.route('/invoices', methods=['GET', 'POST'])
389 def api_invoices():
390 
391  if request.method == 'GET':
392 
393  is_paid = request.args.get('is_paid', None)
394  is_active = request.args.get('is_active', None)
395  date_due_to = request.args.get('date_due_to', None)
396  date_due_from = request.args.get('date_due_from', None)
397 
398  if is_paid == '1':
399  is_paid = 1
400  elif is_paid == '0':
401  is_paid = 0
402  else:
403  is_paid = None
404 
405  if is_active == '1':
406  is_active = 1
407  elif is_active == '0':
408  is_active = 0
409  else:
410  is_active = None
411 
412  invoices = getInvoices(session.book, None, is_paid, is_active,
413  date_due_from, date_due_to)
414 
415  return Response(json.dumps(invoices), mimetype='application/json')
416 
417  elif request.method == 'POST':
418 
419  id = str(request.form.get('id', None))
420 
421  if id == '':
422  id = None
423  elif id != None:
424  id = str(id)
425 
426  customer_id = str(request.form.get('customer_id', ''))
427  currency = str(request.form.get('currency', ''))
428  date_opened = str(request.form.get('date_opened', ''))
429  notes = str(request.form.get('notes', ''))
430 
431  try:
432  invoice = addInvoice(session.book, id, customer_id, currency,
433  date_opened, notes)
434  except Error as error:
435  return Response(json.dumps({'errors': [{'type' : error.type,
436  'message': error.message, 'data': error.data}]}), status=400,
437  mimetype='application/json')
438  else:
439  return Response(json.dumps(invoice), status=201,
440  mimetype='application/json')
441 
442  else:
443  abort(405)
444 
445 @app.route('/invoices/<id>', methods=['GET', 'POST', 'PAY'])
446 def api_invoice(id):
447 
448  if request.method == 'GET':
449 
450  invoice = getInvoice(session.book, id)
451 
452  if invoice is None:
453  abort(404)
454  else:
455  return Response(json.dumps(invoice), mimetype='application/json')
456 
457  elif request.method == 'POST':
458 
459  customer_id = str(request.form.get('customer_id', ''))
460  currency = str(request.form.get('currency', ''))
461  date_opened = request.form.get('date_opened', None)
462  notes = str(request.form.get('notes', ''))
463  posted = request.form.get('posted', None)
464  posted_account_guid = str(request.form.get('posted_account_guid', ''))
465  posted_date = request.form.get('posted_date', '')
466  due_date = request.form.get('due_date', '')
467  posted_memo = str(request.form.get('posted_memo', ''))
468  posted_accumulatesplits = request.form.get('posted_accumulatesplits',
469  '')
470  posted_autopay = request.form.get('posted_autopay', '')
471 
472  if posted == '1':
473  posted = 1
474  else:
475  posted = 0
476 
477  if (posted_accumulatesplits == '1'
478  or posted_accumulatesplits == 'true'
479  or posted_accumulatesplits == 'True'
480  or posted_accumulatesplits == True):
481  posted_accumulatesplits = True
482  else:
483  posted_accumulatesplits = False
484 
485  if posted_autopay == '1':
486  posted_autopay = True
487  else:
488  posted_autopay = False
489  try:
490  invoice = updateInvoice(session.book, id, customer_id, currency,
491  date_opened, notes, posted, posted_account_guid, posted_date,
492  due_date, posted_memo, posted_accumulatesplits, posted_autopay)
493  except Error as error:
494  return Response(json.dumps({'errors': [{'type' : error.type,
495  'message': error.message, 'data': error.data}]}), status=400,
496  mimetype='application/json')
497  else:
498  return Response(json.dumps(invoice), status=200,
499  mimetype='application/json')
500 
501  if invoice is None:
502  abort(404)
503  else:
504  return Response(json.dumps(invoice), mimetype='application/json')
505 
506  elif request.method == 'PAY':
507 
508  posted_account_guid = str(request.form.get('posted_account_guid', ''))
509  transfer_account_guid = str(request.form.get('transfer_account_guid',
510  ''))
511  payment_date = request.form.get('payment_date', '')
512  num = str(request.form.get('num', ''))
513  memo = str(request.form.get('posted_memo', ''))
514  auto_pay = request.form.get('auto_pay', '')
515 
516  try:
517  invoice = payInvoice(session.book, id, posted_account_guid,
518  transfer_account_guid, payment_date, memo, num, auto_pay)
519  except Error as error:
520  return Response(json.dumps({'errors': [{'type' : error.type,
521  'message': error.message, 'data': error.data}]}), status=400,
522  mimetype='application/json')
523  else:
524  return Response(json.dumps(invoice), status=200,
525  mimetype='application/json')
526 
527  else:
528  abort(405)
529 
530 @app.route('/invoices/<id>/entries', methods=['GET', 'POST'])
531 def api_invoice_entries(id):
532 
533  invoice = getInvoice(session.book, id)
534 
535  if invoice is None:
536  abort(404)
537  else:
538  if request.method == 'GET':
539  return Response(json.dumps(invoice['entries']),
540  mimetype='application/json')
541  elif request.method == 'POST':
542 
543  date = str(request.form.get('date', ''))
544  description = str(request.form.get('description', ''))
545  account_guid = str(request.form.get('account_guid', ''))
546  quantity = str(request.form.get('quantity', ''))
547  price = str(request.form.get('price', ''))
548 
549  try:
550  entry = addEntry(session.book, id, date, description,
551  account_guid, quantity, price)
552  except Error as error:
553  return Response(json.dumps({'errors': [{'type' : error.type,
554  'message': error.message, 'data': error.data}]}),
555  status=400, mimetype='application/json')
556  else:
557  return Response(json.dumps(entry), status=201,
558  mimetype='application/json')
559 
560  else:
561  abort(405)
562 
563 @app.route('/entries/<guid>', methods=['GET', 'POST', 'DELETE'])
564 def api_entry(guid):
565 
566  entry = getEntry(session.book, guid)
567 
568  if entry is None:
569  abort(404)
570  else:
571  if request.method == 'GET':
572  return Response(json.dumps(entry), mimetype='application/json')
573  elif request.method == 'POST':
574 
575  date = str(request.form.get('date', ''))
576  description = str(request.form.get('description', ''))
577  account_guid = str(request.form.get('account_guid', ''))
578  quantity = str(request.form.get('quantity', ''))
579  price = str(request.form.get('price', ''))
580 
581  try:
582  entry = updateEntry(session.book, guid, date, description,
583  account_guid, quantity, price)
584  except Error as error:
585  return Response(json.dumps({'errors': [{'type' : error.type,
586  'message': error.message, 'data': error.data}]}),
587  status=400, mimetype='application/json')
588  else:
589  return Response(json.dumps(entry), status=200,
590  mimetype='application/json')
591 
592  elif request.method == 'DELETE':
593 
594  deleteEntry(session.book, guid)
595 
596  return Response('', status=201, mimetype='application/json')
597 
598  else:
599  abort(405)
600 
601 @app.route('/customers', methods=['GET', 'POST'])
602 def api_customers():
603 
604  if request.method == 'GET':
605  customers = getCustomers(session.book)
606  return Response(json.dumps(customers), mimetype='application/json')
607  elif request.method == 'POST':
608 
609  id = str(request.form.get('id', None))
610 
611  if id == '':
612  id = None
613  elif id != None:
614  id = str(id)
615 
616  currency = str(request.form.get('currency', ''))
617  name = str(request.form.get('name', ''))
618  contact = str(request.form.get('contact', ''))
619  address_line_1 = str(request.form.get('address_line_1', ''))
620  address_line_2 = str(request.form.get('address_line_2', ''))
621  address_line_3 = str(request.form.get('address_line_3', ''))
622  address_line_4 = str(request.form.get('address_line_4', ''))
623  phone = str(request.form.get('phone', ''))
624  fax = str(request.form.get('fax', ''))
625  email = str(request.form.get('email', ''))
626 
627  try:
628  customer = addCustomer(session.book, id, currency, name, contact,
629  address_line_1, address_line_2, address_line_3, address_line_4,
630  phone, fax, email)
631  except Error as error:
632  return Response(json.dumps({'errors': [{'type' : error.type,
633  'message': error.message, 'data': error.data}]}), status=400,
634  mimetype='application/json')
635  else:
636  return Response(json.dumps(customer), status=201,
637  mimetype='application/json')
638 
639  else:
640  abort(405)
641 
642 @app.route('/customers/<id>', methods=['GET', 'POST'])
643 def api_customer(id):
644 
645  if request.method == 'GET':
646 
647  customer = getCustomer(session.book, id)
648 
649  if customer is None:
650  abort(404)
651  else:
652  return Response(json.dumps(customer), mimetype='application/json')
653 
654  elif request.method == 'POST':
655 
656  id = str(request.form.get('id', None))
657 
658  name = str(request.form.get('name', ''))
659  contact = str(request.form.get('contact', ''))
660  address_line_1 = str(request.form.get('address_line_1', ''))
661  address_line_2 = str(request.form.get('address_line_2', ''))
662  address_line_3 = str(request.form.get('address_line_3', ''))
663  address_line_4 = str(request.form.get('address_line_4', ''))
664  phone = str(request.form.get('phone', ''))
665  fax = str(request.form.get('fax', ''))
666  email = str(request.form.get('email', ''))
667 
668  try:
669  customer = updateCustomer(session.book, id, name, contact,
670  address_line_1, address_line_2, address_line_3, address_line_4,
671  phone, fax, email)
672  except Error as error:
673  if error.type == 'NoCustomer':
674  return Response(json.dumps({'errors': [{'type' : error.type,
675  'message': error.message, 'data': error.data}]}),
676  status=404, mimetype='application/json')
677  else:
678  return Response(json.dumps({'errors': [{'type' : error.type,
679  'message': error.message, 'data': error.data}]}),
680  status=400, mimetype='application/json')
681  else:
682  return Response(json.dumps(customer), status=200,
683  mimetype='application/json')
684 
685  else:
686  abort(405)
687 
688 @app.route('/customers/<id>/invoices', methods=['GET'])
689 def api_customer_invoices(id):
690 
691  customer = getCustomer(session.book, id)
692 
693  if customer is None:
694  abort(404)
695 
696  invoices = getInvoices(session.book, customer['guid'], None, None, None,
697  None)
698 
699  return Response(json.dumps(invoices), mimetype='application/json')
700 
701 @app.route('/vendors', methods=['GET', 'POST'])
702 def api_vendors():
703 
704  if request.method == 'GET':
705  vendors = getVendors(session.book)
706  return Response(json.dumps(vendors), mimetype='application/json')
707  elif request.method == 'POST':
708 
709  id = str(request.form.get('id', None))
710 
711  if id == '':
712  id = None
713  elif id != None:
714  id = str(id)
715 
716  currency = str(request.form.get('currency', ''))
717  name = str(request.form.get('name', ''))
718  contact = str(request.form.get('contact', ''))
719  address_line_1 = str(request.form.get('address_line_1', ''))
720  address_line_2 = str(request.form.get('address_line_2', ''))
721  address_line_3 = str(request.form.get('address_line_3', ''))
722  address_line_4 = str(request.form.get('address_line_4', ''))
723  phone = str(request.form.get('phone', ''))
724  fax = str(request.form.get('fax', ''))
725  email = str(request.form.get('email', ''))
726 
727  try:
728  vendor = addVendor(session.book, id, currency, name, contact,
729  address_line_1, address_line_2, address_line_3, address_line_4,
730  phone, fax, email)
731  except Error as error:
732  return Response(json.dumps({'errors': [{'type' : error.type,
733  'message': error.message, 'data': error.data}]}), status=400,
734  mimetype='application/json')
735  else:
736  return Response(json.dumps(vendor), status=201,
737  mimetype='application/json')
738 
739  else:
740  abort(405)
741 
742 @app.route('/vendors/<id>', methods=['GET', 'POST'])
743 def api_vendor(id):
744 
745  if request.method == 'GET':
746 
747  vendor = getVendor(session.book, id)
748 
749  if vendor is None:
750  abort(404)
751  else:
752  return Response(json.dumps(vendor), mimetype='application/json')
753  else:
754  abort(405)
755 
756 @app.route('/vendors/<id>/bills', methods=['GET'])
757 def api_vendor_bills(id):
758 
759  vendor = getVendor(session.book, id)
760 
761  if vendor is None:
762  abort(404)
763 
764  bills = getBills(session.book, vendor['guid'], None, None, None, None)
765 
766  return Response(json.dumps(bills), mimetype='application/json')
767 
768 def getCustomers(book):
769 
770  query = gnucash.Query()
771  query.search_for('gncCustomer')
772  query.set_book(book)
773  customers = []
774 
775  for result in query.run():
776  customers.append(gnucash_simple.customerToDict(
777  gnucash.gnucash_business.Customer(instance=result)))
778 
779  query.destroy()
780 
781  return customers
782 
783 def getCustomer(book, id):
784 
785  customer = book.CustomerLookupByID(id)
786 
787  if customer is None:
788  return None
789  else:
790  return gnucash_simple.customerToDict(customer)
791 
792 def getVendors(book):
793 
794  query = gnucash.Query()
795  query.search_for('gncVendor')
796  query.set_book(book)
797  vendors = []
798 
799  for result in query.run():
800  vendors.append(gnucash_simple.vendorToDict(
801  gnucash.gnucash_business.Vendor(instance=result)))
802 
803  query.destroy()
804 
805  return vendors
806 
807 def getVendor(book, id):
808 
809  vendor = book.VendorLookupByID(id)
810 
811  if vendor is None:
812  return None
813  else:
814  return gnucash_simple.vendorToDict(vendor)
815 
816 def getAccounts(book):
817 
818  accounts = gnucash_simple.accountToDict(book.get_root_account())
819 
820  return accounts
821 
822 def getAccountsFlat(book):
823 
824  accounts = gnucash_simple.accountToDict(book.get_root_account())
825 
826  flat_accounts = getSubAccounts(accounts)
827 
828  for n, account in enumerate(flat_accounts):
829  account.pop('subaccounts')
830 
831  filtered_flat_account = []
832 
833  type_ids = [9]
834 
835  for n, account in enumerate(flat_accounts):
836  if account['type_id'] in type_ids:
837  filtered_flat_account.append(account)
838  print account['name'] + ' ' + str(account['type_id'])
839 
840  return filtered_flat_account
841 
842 def getSubAccounts(account):
843 
844  flat_accounts = []
845 
846  if 'subaccounts' in account.keys():
847  for n, subaccount in enumerate(account['subaccounts']):
848  flat_accounts.append(subaccount)
849  flat_accounts = flat_accounts + getSubAccounts(subaccount)
850 
851  return flat_accounts
852 
853 def getAccount(book, guid):
854 
855  account_guid = gnucash.gnucash_core.GUID()
856  gnucash.gnucash_core.GUIDString(guid, account_guid)
857 
858  account = account_guid.AccountLookup(book)
859 
860  if account is None:
861  return None
862 
863  account = gnucash_simple.accountToDict(account)
864 
865  if account is None:
866  return None
867  else:
868  return account
869 
870 
871 def getTransaction(book, guid):
872 
873  transaction_guid = gnucash.gnucash_core.GUID()
874  gnucash.gnucash_core.GUIDString(guid, transaction_guid)
875 
876  transaction = transaction_guid.TransactionLookup(book)
877 
878  if transaction is None:
879  return None
880 
881  transaction = gnucash_simple.transactionToDict(transaction, ['splits'])
882 
883  if transaction is None:
884  return None
885  else:
886  return transaction
887 
888 def getTransactions(book, account_guid, date_posted_from, date_posted_to):
889 
890  query = gnucash.Query()
891 
892  query.search_for('Trans')
893  query.set_book(book)
894 
895  transactions = []
896 
897  for transaction in query.run():
898  transactions.append(gnucash_simple.transactionToDict(
899  gnucash.gnucash_business.Transaction(instance=transaction)))
900 
901  query.destroy()
902 
903  return transactions
904 
905 def getAccountSplits(book, guid, date_posted_from, date_posted_to):
906 
907  account_guid = gnucash.gnucash_core.GUID()
908  gnucash.gnucash_core.GUIDString(guid, account_guid)
909 
910  query = gnucash.Query()
911  query.search_for('Split')
912  query.set_book(book)
913 
914  SPLIT_TRANS= 'trans'
915 
916  QOF_DATE_MATCH_NORMAL = 1
917 
918  TRANS_DATE_POSTED = 'date-posted'
919 
920  if date_posted_from != None:
921  pred_data = gnucash.gnucash_core.QueryDatePredicate(
922  QOF_COMPARE_GTE, QOF_DATE_MATCH_NORMAL, datetime.datetime.strptime(
923  date_posted_from, "%Y-%m-%d").date())
924  param_list = [SPLIT_TRANS, TRANS_DATE_POSTED]
925  query.add_term(param_list, pred_data, QOF_QUERY_AND)
926 
927  if date_posted_to != None:
928  pred_data = gnucash.gnucash_core.QueryDatePredicate(
929  QOF_COMPARE_LTE, QOF_DATE_MATCH_NORMAL, datetime.datetime.strptime(
930  date_posted_to, "%Y-%m-%d").date())
931  param_list = [SPLIT_TRANS, TRANS_DATE_POSTED]
932  query.add_term(param_list, pred_data, QOF_QUERY_AND)
933 
934  SPLIT_ACCOUNT = 'account'
935  QOF_PARAM_GUID = 'guid'
936 
937  if guid != None:
938  gnucash.gnucash_core.GUIDString(guid, account_guid)
939  query.add_guid_match(
940  [SPLIT_ACCOUNT, QOF_PARAM_GUID], account_guid, QOF_QUERY_AND)
941 
942  splits = []
943 
944  for split in query.run():
945  splits.append(gnucash_simple.splitToDict(
946  gnucash.gnucash_business.Split(instance=split),
947  ['account', 'transaction', 'other_split']))
948 
949  query.destroy()
950 
951  return splits
952 
953 def getInvoices(book, customer, is_paid, is_active, date_due_from,
954  date_due_to):
955 
956  query = gnucash.Query()
957  query.search_for('gncInvoice')
958  query.set_book(book)
959 
960  if is_paid == 0:
961  query.add_boolean_match([INVOICE_IS_PAID], False, QOF_QUERY_AND)
962  elif is_paid == 1:
963  query.add_boolean_match([INVOICE_IS_PAID], True, QOF_QUERY_AND)
964 
965  # active = JOB_IS_ACTIVE
966  if is_active == 0:
967  query.add_boolean_match(['active'], False, QOF_QUERY_AND)
968  elif is_active == 1:
969  query.add_boolean_match(['active'], True, QOF_QUERY_AND)
970 
971  QOF_PARAM_GUID = 'guid'
972  INVOICE_OWNER = 'owner'
973 
974  if customer != None:
975  customer_guid = gnucash.gnucash_core.GUID()
976  gnucash.gnucash_core.GUIDString(customer, customer_guid)
977  query.add_guid_match(
978  [INVOICE_OWNER, QOF_PARAM_GUID], customer_guid, QOF_QUERY_AND)
979 
980  if date_due_from != None:
981  pred_data = gnucash.gnucash_core.QueryDatePredicate(
982  QOF_COMPARE_GTE, 2, datetime.datetime.strptime(
983  date_due_from, "%Y-%m-%d").date())
984  query.add_term(['date_due'], pred_data, QOF_QUERY_AND)
985 
986  if date_due_to != None:
987  pred_data = gnucash.gnucash_core.QueryDatePredicate(
988  QOF_COMPARE_LTE, 2, datetime.datetime.strptime(
989  date_due_to, "%Y-%m-%d").date())
990  query.add_term(['date_due'], pred_data, QOF_QUERY_AND)
991 
992  # return only invoices (1 = invoices)
993  pred_data = gnucash.gnucash_core.QueryInt32Predicate(QOF_COMPARE_EQUAL, 1)
994  query.add_term([INVOICE_TYPE], pred_data, QOF_QUERY_AND)
995 
996  invoices = []
997 
998  for result in query.run():
999  invoices.append(gnucash_simple.invoiceToDict(
1000  gnucash.gnucash_business.Invoice(instance=result)))
1001 
1002  query.destroy()
1003 
1004  return invoices
1005 
1006 def getBills(book, customer, is_paid, is_active, date_opened_from,
1007  date_opened_to):
1008 
1009  query = gnucash.Query()
1010  query.search_for('gncInvoice')
1011  query.set_book(book)
1012 
1013  if is_paid == 0:
1014  query.add_boolean_match([INVOICE_IS_PAID], False, QOF_QUERY_AND)
1015  elif is_paid == 1:
1016  query.add_boolean_match([INVOICE_IS_PAID], True, QOF_QUERY_AND)
1017 
1018  # active = JOB_IS_ACTIVE
1019  if is_active == 0:
1020  query.add_boolean_match(['active'], False, QOF_QUERY_AND)
1021  elif is_active == 1:
1022  query.add_boolean_match(['active'], True, QOF_QUERY_AND)
1023 
1024  QOF_PARAM_GUID = 'guid'
1025  INVOICE_OWNER = 'owner'
1026 
1027  if customer != None:
1028  customer_guid = gnucash.gnucash_core.GUID()
1029  gnucash.gnucash_core.GUIDString(customer, customer_guid)
1030  query.add_guid_match(
1031  [INVOICE_OWNER, QOF_PARAM_GUID], customer_guid, QOF_QUERY_AND)
1032 
1033  if date_opened_from != None:
1034  pred_data = gnucash.gnucash_core.QueryDatePredicate(
1035  QOF_COMPARE_GTE, 2, datetime.datetime.strptime(
1036  date_opened_from, "%Y-%m-%d").date())
1037  query.add_term(['date_opened'], pred_data, QOF_QUERY_AND)
1038 
1039  if date_opened_to != None:
1040  pred_data = gnucash.gnucash_core.QueryDatePredicate(
1041  QOF_COMPARE_LTE, 2, datetime.datetime.strptime(
1042  date_opened_to, "%Y-%m-%d").date())
1043  query.add_term(['date_opened'], pred_data, QOF_QUERY_AND)
1044 
1045  # return only bills (2 = bills)
1046  pred_data = gnucash.gnucash_core.QueryInt32Predicate(QOF_COMPARE_EQUAL, 2)
1047  query.add_term([INVOICE_TYPE], pred_data, QOF_QUERY_AND)
1048 
1049  bills = []
1050 
1051  for result in query.run():
1052  bills.append(gnucash_simple.billToDict(
1053  gnucash.gnucash_business.Bill(instance=result)))
1054 
1055  query.destroy()
1056 
1057  return bills
1058 
1059 def getGnuCashInvoice(book ,id):
1060 
1061  # we don't use book.InvoicelLookupByID(id) as this is identical to
1062  # book.BillLookupByID(id) so can return the same object if they share IDs
1063 
1064  query = gnucash.Query()
1065  query.search_for('gncInvoice')
1066  query.set_book(book)
1067 
1068  # return only invoices (1 = invoices)
1069  pred_data = gnucash.gnucash_core.QueryInt32Predicate(QOF_COMPARE_EQUAL, 1)
1070  query.add_term([INVOICE_TYPE], pred_data, QOF_QUERY_AND)
1071 
1072  INVOICE_ID = 'id'
1073 
1074  pred_data = gnucash.gnucash_core.QueryStringPredicate(
1075  QOF_COMPARE_EQUAL, id, QOF_STRING_MATCH_NORMAL, False)
1076  query.add_term([INVOICE_ID], pred_data, QOF_QUERY_AND)
1077 
1078  invoice = None
1079 
1080  for result in query.run():
1081  invoice = gnucash.gnucash_business.Invoice(instance=result)
1082 
1083  query.destroy()
1084 
1085  return invoice
1086 
1087 def getGnuCashBill(book ,id):
1088 
1089  # we don't use book.InvoicelLookupByID(id) as this is identical to
1090  # book.BillLookupByID(id) so can return the same object if they share IDs
1091 
1092  query = gnucash.Query()
1093  query.search_for('gncInvoice')
1094  query.set_book(book)
1095 
1096  # return only bills (2 = bills)
1097  pred_data = gnucash.gnucash_core.QueryInt32Predicate(QOF_COMPARE_EQUAL, 2)
1098  query.add_term([INVOICE_TYPE], pred_data, QOF_QUERY_AND)
1099 
1100  INVOICE_ID = 'id'
1101 
1102  pred_data = gnucash.gnucash_core.QueryStringPredicate(
1103  QOF_COMPARE_EQUAL, id, QOF_STRING_MATCH_NORMAL, False)
1104  query.add_term([INVOICE_ID], pred_data, QOF_QUERY_AND)
1105 
1106  bill = None
1107 
1108  for result in query.run():
1109  bill = gnucash.gnucash_business.Bill(instance=result)
1110 
1111  query.destroy()
1112 
1113  return bill
1114 
1115 def getInvoice(book, id):
1116 
1117  return gnucash_simple.invoiceToDict(getGnuCashInvoice(book, id))
1118 
1119 def payInvoice(book, id, posted_account_guid, transfer_account_guid,
1120  payment_date, memo, num, auto_pay):
1121 
1122  invoice = getGnuCashInvoice(book, id)
1123 
1124  account_guid2 = gnucash.gnucash_core.GUID()
1125  gnucash.gnucash_core.GUIDString(transfer_account_guid, account_guid2)
1126 
1127  xfer_acc = account_guid2.AccountLookup(session.book)
1128 
1129  invoice.ApplyPayment(None, xfer_acc, invoice.GetTotal(), GncNumeric(0),
1130  datetime.datetime.strptime(payment_date, '%Y-%m-%d'), memo, num)
1131 
1132  return gnucash_simple.invoiceToDict(invoice)
1133 
1134 def payBill(book, id, posted_account_guid, transfer_account_guid, payment_date,
1135  memo, num, auto_pay):
1136 
1137  bill = getGnuCashBill(book, id)
1138 
1139  account_guid = gnucash.gnucash_core.GUID()
1140  gnucash.gnucash_core.GUIDString(transfer_account_guid, account_guid)
1141 
1142  xfer_acc = account_guid.AccountLookup(session.book)
1143 
1144  # We pay the negitive total as the bill as this seemed to cause issues
1145  # with the split not being set correctly and not being marked as paid
1146  bill.ApplyPayment(None, xfer_acc, bill.GetTotal().neg(), GncNumeric(0),
1147  datetime.datetime.strptime(payment_date, '%Y-%m-%d'), memo, num)
1148 
1149  return gnucash_simple.billToDict(bill)
1150 
1151 def getBill(book, id):
1152 
1153  return gnucash_simple.billToDict(getGnuCashBill(book, id))
1154 
1155 def addVendor(book, id, currency_mnumonic, name, contact, address_line_1,
1156  address_line_2, address_line_3, address_line_4, phone, fax, email):
1157 
1158  if name == '':
1159  raise Error('NoVendorName', 'A name must be entered for this company',
1160  {'field': 'name'})
1161 
1162  if (address_line_1 == ''
1163  and address_line_2 == ''
1164  and address_line_3 == ''
1165  and address_line_4 == ''):
1166  raise Error('NoVendorAddress',
1167  'An address must be entered for this company',
1168  {'field': 'address'})
1169 
1170  commod_table = book.get_table()
1171  currency = commod_table.lookup('CURRENCY', currency_mnumonic)
1172 
1173  if currency is None:
1174  raise Error('InvalidVendorCurrency',
1175  'A valid currency must be supplied for this vendor',
1176  {'field': 'currency'})
1177 
1178  if id is None:
1179  id = book.VendorNextID()
1180 
1181  vendor = Vendor(session.book, id, currency, name)
1182 
1183  address = vendor.GetAddr()
1184  address.SetName(contact)
1185  address.SetAddr1(address_line_1)
1186  address.SetAddr2(address_line_2)
1187  address.SetAddr3(address_line_3)
1188  address.SetAddr4(address_line_4)
1189  address.SetPhone(phone)
1190  address.SetFax(fax)
1191  address.SetEmail(email)
1192 
1193  return gnucash_simple.vendorToDict(vendor)
1194 
1195 def addCustomer(book, id, currency_mnumonic, name, contact, address_line_1,
1196  address_line_2, address_line_3, address_line_4, phone, fax, email):
1197 
1198  if name == '':
1199  raise Error('NoCustomerName',
1200  'A name must be entered for this company', {'field': 'name'})
1201 
1202  if (address_line_1 == ''
1203  and address_line_2 == ''
1204  and address_line_3 == ''
1205  and address_line_4 == ''):
1206  raise Error('NoCustomerAddress',
1207  'An address must be entered for this company',
1208  {'field': 'address'})
1209 
1210  commod_table = book.get_table()
1211  currency = commod_table.lookup('CURRENCY', currency_mnumonic)
1212 
1213  if currency is None:
1214  raise Error('InvalidCustomerCurrency',
1215  'A valid currency must be supplied for this customer',
1216  {'field': 'currency'})
1217 
1218  if id is None:
1219  id = book.CustomerNextID()
1220 
1221  customer = Customer(session.book, id, currency, name)
1222 
1223  address = customer.GetAddr()
1224  address.SetName(contact)
1225  address.SetAddr1(address_line_1)
1226  address.SetAddr2(address_line_2)
1227  address.SetAddr3(address_line_3)
1228  address.SetAddr4(address_line_4)
1229  address.SetPhone(phone)
1230  address.SetFax(fax)
1231  address.SetEmail(email)
1232 
1233  return gnucash_simple.customerToDict(customer)
1234 
1235 def updateCustomer(book, id, name, contact, address_line_1, address_line_2,
1236  address_line_3, address_line_4, phone, fax, email):
1237 
1238  customer = book.CustomerLookupByID(id)
1239 
1240  if customer is None:
1241  raise Error('NoCustomer', 'A customer with this ID does not exist',
1242  {'field': 'id'})
1243 
1244  if name == '':
1245  raise Error('NoCustomerName',
1246  'A name must be entered for this company', {'field': 'name'})
1247 
1248  if (address_line_1 == ''
1249  and address_line_2 == ''
1250  and address_line_3 == ''
1251  and address_line_4 == ''):
1252  raise Error('NoCustomerAddress',
1253  'An address must be entered for this company',
1254  {'field': 'address'})
1255 
1256  customer.SetName(name)
1257 
1258  address = customer.GetAddr()
1259  address.SetName(contact)
1260  address.SetAddr1(address_line_1)
1261  address.SetAddr2(address_line_2)
1262  address.SetAddr3(address_line_3)
1263  address.SetAddr4(address_line_4)
1264  address.SetPhone(phone)
1265  address.SetFax(fax)
1266  address.SetEmail(email)
1267 
1268  return gnucash_simple.customerToDict(customer)
1269 
1270 def addInvoice(book, id, customer_id, currency_mnumonic, date_opened, notes):
1271 
1272  customer = book.CustomerLookupByID(customer_id)
1273 
1274  if customer is None:
1275  raise Error('NoCustomer',
1276  'A customer with this ID does not exist', {'field': 'id'})
1277 
1278  if id is None:
1279  id = book.InvoiceNextID(customer)
1280 
1281  try:
1282  date_opened = datetime.datetime.strptime(date_opened, "%Y-%m-%d")
1283  except ValueError:
1284  raise Error('InvalidDateOpened',
1285  'The date opened must be provided in the form YYYY-MM-DD',
1286  {'field': 'date_opened'})
1287 
1288  if currency_mnumonic is None:
1289  currency_mnumonic = customer.GetCurrency().get_mnemonic()
1290 
1291  commod_table = book.get_table()
1292  currency = commod_table.lookup('CURRENCY', currency_mnumonic)
1293 
1294  if currency is None:
1295  raise Error('InvalidCustomerCurrency',
1296  'A valid currency must be supplied for this customer',
1297  {'field': 'currency'})
1298 
1299  invoice = Invoice(book, id, currency, customer, date_opened.date())
1300 
1301  invoice.SetNotes(notes)
1302 
1303  return gnucash_simple.invoiceToDict(invoice)
1304 
1305 def updateInvoice(book, id, customer_id, currency_mnumonic, date_opened,
1306  notes, posted, posted_account_guid, posted_date, due_date, posted_memo,
1307  posted_accumulatesplits, posted_autopay):
1308 
1309  invoice = getGnuCashInvoice(book, id)
1310 
1311  if invoice is None:
1312  raise Error('NoInvoice',
1313  'An invoice with this ID does not exist',
1314  {'field': 'id'})
1315 
1316  customer = book.CustomerLookupByID(customer_id)
1317 
1318  if customer is None:
1319  raise Error('NoCustomer', 'A customer with this ID does not exist',
1320  {'field': 'customer_id'})
1321 
1322  try:
1323  date_opened = datetime.datetime.strptime(date_opened, "%Y-%m-%d")
1324  except ValueError:
1325  raise Error('InvalidDateOpened',
1326  'The date opened must be provided in the form YYYY-MM-DD',
1327  {'field': 'date_opened'})
1328 
1329  if posted_date == '':
1330  if posted == 1:
1331  raise Error('NoDatePosted',
1332  'The date posted must be supplied when posted=1',
1333  {'field': 'date_posted'})
1334  else:
1335  try:
1336  posted_date = datetime.datetime.strptime(posted_date, "%Y-%m-%d")
1337  except ValueError:
1338  raise Error('InvalidDatePosted',
1339  'The date posted must be provided in the form YYYY-MM-DD',
1340  {'field': 'posted_date'})
1341 
1342  if due_date == '':
1343  if posted == 1:
1344  raise Error('NoDatePosted',
1345  'The due date must be supplied when posted=1',
1346  {'field': 'date_posted'})
1347  else:
1348  try:
1349  due_date = datetime.datetime.strptime(due_date, "%Y-%m-%d")
1350  except ValueError:
1351  raise Error('InvalidDatePosted',
1352  'The due date must be provided in the form YYYY-MM-DD',
1353  {'field': 'due_date'})
1354 
1355  if posted_account_guid == '':
1356  if posted == 1:
1357  raise Error('NoPostedAccountGuid',
1358  'The posted account GUID must be supplied when posted=1',
1359  {'field': 'posted_account_guid'})
1360  else:
1361  guid = gnucash.gnucash_core.GUID()
1362  gnucash.gnucash_core.GUIDString(posted_account_guid, guid)
1363 
1364  posted_account = guid.AccountLookup(book)
1365 
1366  if posted_account is None:
1367  raise Error('NoAccount',
1368  'No account exists with the posted account GUID',
1369  {'field': 'posted_account_guid'})
1370 
1371  invoice.SetOwner(customer)
1372  invoice.SetDateOpened(date_opened)
1373  invoice.SetNotes(notes)
1374 
1375  # post if currently unposted and posted=1
1376  if (invoice.GetDatePosted().strftime('%Y-%m-%d') == '1970-01-01'
1377  and posted == 1):
1378  invoice.PostToAccount(posted_account, posted_date, due_date,
1379  posted_memo, posted_accumulatesplits, posted_autopay)
1380 
1381  return gnucash_simple.invoiceToDict(invoice)
1382 
1383 def updateBill(book, id, vendor_id, currency_mnumonic, date_opened, notes,
1384  posted, posted_account_guid, posted_date, due_date, posted_memo,
1385  posted_accumulatesplits, posted_autopay):
1386 
1387  bill = getGnuCashBill(book, id)
1388 
1389  if bill is None:
1390  raise Error('NoBill', 'A bill with this ID does not exist',
1391  {'field': 'id'})
1392 
1393  vendor = book.VendorLookupByID(vendor_id)
1394 
1395  if vendor is None:
1396  raise Error('NoVendor',
1397  'A vendor with this ID does not exist',
1398  {'field': 'vendor_id'})
1399 
1400  try:
1401  date_opened = datetime.datetime.strptime(date_opened, "%Y-%m-%d")
1402  except ValueError:
1403  raise Error('InvalidDateOpened',
1404  'The date opened must be provided in the form YYYY-MM-DD',
1405  {'field': 'date_opened'})
1406 
1407  if posted_date == '':
1408  if posted == 1:
1409  raise Error('NoDatePosted',
1410  'The date posted must be supplied when posted=1',
1411  {'field': 'date_posted'})
1412  else:
1413  try:
1414  posted_date = datetime.datetime.strptime(posted_date, "%Y-%m-%d")
1415  except ValueError:
1416  raise Error('InvalidDatePosted',
1417  'The date posted must be provided in the form YYYY-MM-DD',
1418  {'field': 'posted_date'})
1419 
1420  if due_date == '':
1421  if posted == 1:
1422  raise Error('NoDatePosted',
1423  'The due date must be supplied when posted=1',
1424  {'field': 'date_posted'})
1425  else:
1426  try:
1427  due_date = datetime.datetime.strptime(due_date, "%Y-%m-%d")
1428  except ValueError:
1429  raise Error('InvalidDatePosted',
1430  'The due date must be provided in the form YYYY-MM-DD',
1431  {'field': 'due_date'})
1432 
1433  if posted_account_guid == '':
1434  if posted == 1:
1435  raise Error('NoPostedAccountGuid',
1436  'The posted account GUID must be supplied when posted=1',
1437  {'field': 'posted_account_guid'})
1438  else:
1439  guid = gnucash.gnucash_core.GUID()
1440  gnucash.gnucash_core.GUIDString(posted_account_guid, guid)
1441 
1442  posted_account = guid.AccountLookup(book)
1443 
1444  if posted_account is None:
1445  raise Error('NoAccount',
1446  'No account exists with the posted account GUID',
1447  {'field': 'posted_account_guid'})
1448 
1449  bill.SetOwner(vendor)
1450  bill.SetDateOpened(date_opened)
1451  bill.SetNotes(notes)
1452 
1453  # post if currently unposted and posted=1
1454  if bill.GetDatePosted().strftime('%Y-%m-%d') == '1970-01-01' and posted == 1:
1455  bill.PostToAccount(posted_account, posted_date, due_date, posted_memo,
1456  posted_accumulatesplits, posted_autopay)
1457 
1458  return gnucash_simple.billToDict(bill)
1459 
1460 def addEntry(book, invoice_id, date, description, account_guid, quantity, price):
1461 
1462  invoice = getGnuCashInvoice(book, invoice_id)
1463 
1464  if invoice is None:
1465  raise Error('NoInvoice',
1466  'No invoice exists with this ID', {'field': 'invoice_id'})
1467 
1468  try:
1469  date = datetime.datetime.strptime(date, "%Y-%m-%d")
1470  except ValueError:
1471  raise Error('InvalidDateOpened',
1472  'The date opened must be provided in the form YYYY-MM-DD',
1473  {'field': 'date'})
1474 
1475  guid = gnucash.gnucash_core.GUID()
1476  gnucash.gnucash_core.GUIDString(account_guid, guid)
1477 
1478  account = guid.AccountLookup(book)
1479 
1480  if account is None:
1481  raise Error('NoAccount', 'No account exists with this GUID',
1482  {'field': 'account_guid'})
1483 
1484  try:
1485  quantity = Decimal(quantity).quantize(Decimal('.01'))
1486  except ArithmeticError:
1487  raise Error('InvalidQuantity', 'This quantity is not valid',
1488  {'field': 'quantity'})
1489 
1490  try:
1491  price = Decimal(price).quantize(Decimal('.01'))
1492  except ArithmeticError:
1493  raise Error('InvalidPrice', 'This price is not valid',
1494  {'field': 'price'})
1495 
1496  entry = Entry(book, invoice, date.date())
1497  entry.SetDateEntered(datetime.datetime.now())
1498  entry.SetDescription(description)
1499  entry.SetInvAccount(account)
1500  entry.SetQuantity(gnc_numeric_from_decimal(quantity))
1501  entry.SetInvPrice(gnc_numeric_from_decimal(price))
1502 
1503  return gnucash_simple.entryToDict(entry)
1504 
1505 def addBillEntry(book, bill_id, date, description, account_guid, quantity,
1506  price):
1507 
1508  bill = getGnuCashBill(book,bill_id)
1509 
1510  if bill is None:
1511  raise Error('NoBill', 'No bill exists with this ID',
1512  {'field': 'bill_id'})
1513 
1514  try:
1515  date = datetime.datetime.strptime(date, "%Y-%m-%d")
1516  except ValueError:
1517  raise Error('InvalidDateOpened',
1518  'The date opened must be provided in the form YYYY-MM-DD',
1519  {'field': 'date'})
1520 
1521  guid = gnucash.gnucash_core.GUID()
1522  gnucash.gnucash_core.GUIDString(account_guid, guid)
1523 
1524  account = guid.AccountLookup(book)
1525 
1526  if account is None:
1527  raise Error('NoAccount', 'No account exists with this GUID',
1528  {'field': 'account_guid'})
1529 
1530  try:
1531  quantity = Decimal(quantity).quantize(Decimal('.01'))
1532  except ArithmeticError:
1533  raise Error('InvalidQuantity', 'This quantity is not valid',
1534  {'field': 'quantity'})
1535 
1536  try:
1537  price = Decimal(price).quantize(Decimal('.01'))
1538  except ArithmeticError:
1539  raise Error('InvalidPrice', 'This price is not valid',
1540  {'field': 'price'})
1541 
1542  entry = Entry(book, bill, date.date())
1543  entry.SetDateEntered(datetime.datetime.now())
1544  entry.SetDescription(description)
1545  entry.SetBillAccount(account)
1546  entry.SetQuantity(gnc_numeric_from_decimal(quantity))
1547  entry.SetBillPrice(gnc_numeric_from_decimal(price))
1548 
1549  return gnucash_simple.entryToDict(entry)
1550 
1551 def getEntry(book, entry_guid):
1552 
1553  guid = gnucash.gnucash_core.GUID()
1554  gnucash.gnucash_core.GUIDString(entry_guid, guid)
1555 
1556  entry = book.EntryLookup(guid)
1557 
1558  if entry is None:
1559  return None
1560  else:
1561  return gnucash_simple.entryToDict(entry)
1562 
1563 def updateEntry(book, entry_guid, date, description, account_guid, quantity,
1564  price):
1565 
1566  guid = gnucash.gnucash_core.GUID()
1567  gnucash.gnucash_core.GUIDString(entry_guid, guid)
1568 
1569  entry = book.EntryLookup(guid)
1570 
1571  if entry is None:
1572  raise Error('NoEntry', 'No entry exists with this GUID',
1573  {'field': 'entry_guid'})
1574 
1575  try:
1576  date = datetime.datetime.strptime(date, "%Y-%m-%d")
1577  except ValueError:
1578  raise Error('InvalidDateOpened',
1579  'The date opened must be provided in the form YYYY-MM-DD',
1580  {'field': 'date'})
1581 
1582  gnucash.gnucash_core.GUIDString(account_guid, guid)
1583 
1584  account = guid.AccountLookup(book)
1585 
1586  if account is None:
1587  raise Error('NoAccount', 'No account exists with this GUID',
1588  {'field': 'account_guid'})
1589 
1590  entry.SetDate(date.date())
1591  entry.SetDateEntered(datetime.datetime.now())
1592  entry.SetDescription(description)
1593  entry.SetInvAccount(account)
1594  entry.SetQuantity(
1595  gnc_numeric_from_decimal(Decimal(quantity).quantize(Decimal('.01'))))
1596  entry.SetInvPrice(
1597  gnc_numeric_from_decimal(Decimal(price).quantize(Decimal('.01'))))
1598 
1599  return gnucash_simple.entryToDict(entry)
1600 
1601 def deleteEntry(book, entry_guid):
1602 
1603  guid = gnucash.gnucash_core.GUID()
1604  gnucash.gnucash_core.GUIDString(entry_guid, guid)
1605 
1606  entry = book.EntryLookup(guid)
1607 
1608  invoice = entry.GetInvoice()
1609  bill = entry.GetBill()
1610 
1611  if invoice != None and entry != None:
1612  invoice.RemoveEntry(entry)
1613  elif bill != None and entry != None:
1614  bill.RemoveEntry(entry)
1615 
1616  if entry != None:
1617  entry.Destroy()
1618 
1619 def deleteTransaction(book, transaction_guid):
1620 
1621  guid = gnucash.gnucash_core.GUID()
1622  gnucash.gnucash_core.GUIDString(transaction_guid, guid)
1623 
1624  transaction = guid.TransLookup(book)
1625 
1626  if transaction != None :
1627  transaction.Destroy()
1628 
1629 def addBill(book, id, vendor_id, currency_mnumonic, date_opened, notes):
1630 
1631  vendor = book.VendorLookupByID(vendor_id)
1632 
1633  if vendor is None:
1634  raise Error('NoVendor', 'A vendor with this ID does not exist',
1635  {'field': 'id'})
1636 
1637  if id is None:
1638  id = book.BillNextID(vendor)
1639 
1640  try:
1641  date_opened = datetime.datetime.strptime(date_opened, "%Y-%m-%d")
1642  except ValueError:
1643  raise Error('InvalidVendorDateOpened',
1644  'The date opened must be provided in the form YYYY-MM-DD',
1645  {'field': 'date_opened'})
1646 
1647  if currency_mnumonic is None:
1648  currency_mnumonic = vendor.GetCurrency().get_mnemonic()
1649 
1650  commod_table = book.get_table()
1651  currency = commod_table.lookup('CURRENCY', currency_mnumonic)
1652 
1653  if currency is None:
1654  raise Error('InvalidVendorCurrency',
1655  'A valid currency must be supplied for this vendor',
1656  {'field': 'currency'})
1657 
1658  bill = Bill(book, id, currency, vendor, date_opened.date())
1659 
1660  bill.SetNotes(notes)
1661 
1662  return gnucash_simple.billToDict(bill)
1663 
1664 def addAccount(book, name, currency_mnumonic, account_guid):
1665 
1666  from gnucash.gnucash_core_c import \
1667  ACCT_TYPE_ASSET, ACCT_TYPE_RECEIVABLE, ACCT_TYPE_INCOME, \
1668  GNC_OWNER_CUSTOMER, ACCT_TYPE_LIABILITY
1669 
1670  root_account = book.get_root_account()
1671 
1672  commod_table = book.get_table()
1673  currency = commod_table.lookup('CURRENCY', currency_mnumonic)
1674 
1675  if currency is None:
1676  raise Error('InvalidCustomerCurrency',
1677  'A valid currency must be supplied for this customer',
1678  {'field': 'currency'})
1679 
1680  account = Account(book)
1681  root_account.append_child(root_account)
1682  account.SetName(name)
1683  account.SetType(ACCT_TYPE_ASSET)
1684  account.SetCommodity(currency)
1685 
1686 def addTransaction(book, num, description, date_posted, currency_mnumonic, splits):
1687 
1688  transaction = Transaction(book)
1689 
1690  transaction.BeginEdit()
1691 
1692  commod_table = book.get_table()
1693  currency = commod_table.lookup('CURRENCY', currency_mnumonic)
1694 
1695  if currency is None:
1696  raise Error('InvalidTransactionCurrency',
1697  'A valid currency must be supplied for this transaction',
1698  {'field': 'currency'})
1699 
1700  try:
1701  date_posted = datetime.datetime.strptime(date_posted, "%Y-%m-%d")
1702  except ValueError:
1703  raise Error('InvalidDatePosted',
1704  'The date posted must be provided in the form YYYY-MM-DD',
1705  {'field': 'date_posted'})
1706 
1707 
1708  for split_values in splits:
1709  account_guid = gnucash.gnucash_core.GUID()
1710  gnucash.gnucash_core.GUIDString(split_values['account_guid'], account_guid)
1711 
1712  account = account_guid.AccountLookup(book)
1713 
1714  if account is None:
1715  raise Error('InvalidSplitAccount',
1716  'A valid account must be supplied for this split',
1717  {'field': 'account'})
1718 
1719  split = Split(book)
1720  split.SetValue(GncNumeric(split_values['value'], 100))
1721  split.SetAccount(account)
1722  split.SetParent(transaction)
1723 
1724  transaction.SetCurrency(currency)
1725  transaction.SetDescription(description)
1726  transaction.SetNum(num)
1727 
1728  transaction.SetDatePostedTS(date_posted)
1729 
1730  transaction.CommitEdit()
1731 
1732  return gnucash_simple.transactionToDict(transaction, ['splits'])
1733 
1734 def getTransaction(book, transaction_guid):
1735 
1736  guid = gnucash.gnucash_core.GUID()
1737  gnucash.gnucash_core.GUIDString(transaction_guid, guid)
1738 
1739  transaction = guid.TransLookup(book)
1740 
1741  if transaction is None:
1742  return None
1743  else:
1744  return gnucash_simple.transactionToDict(transaction, ['splits'])
1745 
1746 def editTransaction(book, transaction_guid, num, description, date_posted,
1747  currency_mnumonic, splits):
1748 
1749  guid = gnucash.gnucash_core.GUID()
1750  gnucash.gnucash_core.GUIDString(transaction_guid, guid)
1751 
1752  transaction = guid.TransLookup(book)
1753 
1754  if transaction is None:
1755  raise Error('NoCustomer',
1756  'A transaction with this GUID does not exist',
1757  {'field': 'guid'})
1758 
1759  transaction.BeginEdit()
1760 
1761  commod_table = book.get_table()
1762  currency = commod_table.lookup('CURRENCY', currency_mnumonic)
1763 
1764  if currency is None:
1765  raise Error('InvalidTransactionCurrency',
1766  'A valid currency must be supplied for this transaction',
1767  {'field': 'currency'})
1768 
1769 
1770  try:
1771  date_posted = datetime.datetime.strptime(date_posted, "%Y-%m-%d")
1772  except ValueError:
1773  raise Error('InvalidDatePosted',
1774  'The date posted must be provided in the form YYYY-MM-DD',
1775  {'field': 'date_posted'})
1776 
1777  for split_values in splits:
1778 
1779  split_guid = gnucash.gnucash_core.GUID()
1780  gnucash.gnucash_core.GUIDString(split_values['guid'], split_guid)
1781 
1782  split = split_guid.SplitLookup(book)
1783 
1784  if split is None:
1785  raise Error('InvalidSplitGuid',
1786  'A valid guid must be supplied for this split',
1787  {'field': 'guid'})
1788 
1789  account_guid = gnucash.gnucash_core.GUID()
1790  gnucash.gnucash_core.GUIDString(
1791  split_values['account_guid'], account_guid)
1792 
1793  account = account_guid.AccountLookup(book)
1794 
1795  if account is None:
1796  raise Error('InvalidSplitAccount',
1797  'A valid account must be supplied for this split',
1798  {'field': 'account'})
1799 
1800  split.SetValue(GncNumeric(split_values['value'], 100))
1801  split.SetAccount(account)
1802  split.SetParent(transaction)
1803 
1804  transaction.SetCurrency(currency)
1805  transaction.SetDescription(description)
1806  transaction.SetNum(num)
1807 
1808  transaction.SetDatePostedTS(date_posted)
1809 
1810  transaction.CommitEdit()
1811 
1812  return gnucash_simple.transactionToDict(transaction, ['splits'])
1813 
1814 def gnc_numeric_from_decimal(decimal_value):
1815  sign, digits, exponent = decimal_value.as_tuple()
1816 
1817  # convert decimal digits to a fractional numerator
1818  # equivlent to
1819  # numerator = int(''.join(digits))
1820  # but without the wated conversion to string and back,
1821  # this is probably the same algorithm int() uses
1822  numerator = 0
1823  TEN = int(Decimal(0).radix()) # this is always 10
1824  numerator_place_value = 1
1825  # add each digit to the final value multiplied by the place value
1826  # from least significant to most sigificant
1827  for i in xrange(len(digits)-1,-1,-1):
1828  numerator += digits[i] * numerator_place_value
1829  numerator_place_value *= TEN
1830 
1831  if decimal_value.is_signed():
1832  numerator = -numerator
1833 
1834  # if the exponent is negative, we use it to set the denominator
1835  if exponent < 0 :
1836  denominator = TEN ** (-exponent)
1837  # if the exponent isn't negative, we bump up the numerator
1838  # and set the denominator to 1
1839  else:
1840  numerator *= TEN ** exponent
1841  denominator = 1
1842 
1843  return GncNumeric(numerator, denominator)
1844 
1845 def shutdown():
1846  session.save()
1847  session.end()
1848  session.destroy()
1849  print 'Shutdown'
1850 
1851 class Error(Exception):
1852  """Base class for exceptions in this module."""
1853  def __init__(self, type, message, data):
1854  self.type = type
1855  self.message = message
1856  self.data = data
1857 
1858 try:
1859  options, arguments = getopt.getopt(sys.argv[1:], 'nh:', ['host=', 'new='])
1860 except getopt.GetoptError as err:
1861  print str(err) # will print something like "option -a not recognized"
1862  print 'Usage: python-rest.py <connection string>'
1863  sys.exit(2)
1864 
1865 if len(arguments) == 0:
1866  print 'Usage: python-rest.py <connection string>'
1867  sys.exit(2)
1868 
1869 #set default host for Flask
1870 host = '127.0.0.1'
1871 
1872 #allow host option to be changed
1873 for option, value in options:
1874  if option in ("-h", "--host"):
1875  host = value
1876 
1877 is_new = False
1878 
1879 # allow a new database to be used
1880 for option, value in options:
1881  if option in ("-n", "--new"):
1882  is_new = True
1883 
1884 
1885 #start gnucash session base on connection string argument
1886 if is_new:
1887  session = gnucash.Session(arguments[0], is_new=True)
1888 
1889  # seem to get errors if we use the session directly, so save it and
1890  #destroy it so it's no longer new
1891 
1892  session.save()
1893  session.end()
1894  session.destroy()
1895 
1896 session = gnucash.Session(arguments[0], ignore_lock=True)
1897 
1898 # register method to close gnucash connection gracefully
1899 atexit.register(shutdown)
1900 
1901 app.debug = False
1902 
1903 # log to console
1904 if not app.debug:
1905  import logging
1906  from logging import StreamHandler
1907  stream_handler = StreamHandler()
1908  stream_handler.setLevel(logging.ERROR)
1909  app.logger.addHandler(stream_handler)
1910 
1911 # start Flask server
1912 app.run(host=host)
Definition: SplitP.h:71