gnucash master: - REST API Example for Python Bindings #2

John Ralls jralls at code.gnucash.org
Mon Jan 5 22:19:42 EST 2015


Updated	 via  https://github.com/Gnucash/gnucash/commit/c27bea60 (commit)
	from  https://github.com/Gnucash/gnucash/commit/39b4034e (commit)



commit c27bea603132b4b0d3ca82b324dcfe12b46814f9
Author: Tom Lofts <dev at loftx.co.uk>
Date:   Sun Jan 4 00:52:05 2015 +0000

    - REST API Example for Python Bindings #2
    
    Enhancements include:
    - manipulation of transactions, splits, vendors and bills
    - add and edit functionality customers and invoices
    
    Author: Tom Lofts <dev at loftx.co.uk>

diff --git a/src/optional/python-bindings/example_scripts/rest-api/README b/src/optional/python-bindings/example_scripts/rest-api/README
index b15d8dd..393035f 100644
--- a/src/optional/python-bindings/example_scripts/rest-api/README
+++ b/src/optional/python-bindings/example_scripts/rest-api/README
@@ -1,48 +1,218 @@
 A Python based REST framework for the Gnucash accounting application
 
-**Please note this is a very early work in progress and should not be run against live or production data.**
-
-This project is designed to allow other applications to easily extract (and hopefully insert and update at some point) data from Gnucash via a RESTful API.
-
-The API is built on the existing Python bindings and uses Flask to serve responses in JSON.
-
-Currently only accounts, customers and invoices can be accessed via the framework
-
-Accounts: <http://localhost:5000/accounts>
-Individual accounr: <http://localhost:5000/accounts/XXXX>
-
-Invoices: <http://localhost:5000/invoices>
-Individual invoice: <http://localhost:5000/invoices/XXXX>
-
-Customers: <http://localhost:5000/customers>
-Individual customer: <http://localhost:5000/customers/XXXX>
-
-Invoices can be filtered via the following parameters:
-
-is_active 1 or 0
-is_paid 1 or 0
-
-e.g. <http://localhost:5000/invoices?is_active=1&is_paid=0>
+**Please note this is a very early work in progress and should not be run
+against live or production data.**
+
+This project is designed to allow other applications to easily interact with
+data from Gnucash via a RESTful API.
+
+The API is built on the existing Python bindings and uses Flask to serve
+responses in JSON.
+
+Currently accounts, transactions, splits, customers, vendors, invoices and
+bills can be accessed to some degree via the framework:
+
+Accounts:
+
+GET request to /accounts - returns a complete list of accounts
+GET request to /accounts/<guid> - returns an individual account
+GET request to /accounts/<guid>/splits - returns splits for an individual
+account
+
+POST request to /transactions - creates a new transaction (currently with only
+two splits)
+
+POST variables:
+currency  - required - a gnucash currency code e.g. GBP
+description - required - a description for the transaction
+num - optional - a transaction number
+date_posted - required - a date formatted as YYYY-MM-DD
+splitvalue1 - required - the value of the split on splitaccount1 
+splitaccount1 - required - the GUID for the 1st account on this transaction
+(can be obtained from /accounts)
+splitvalue2 - required - the value of the split on splitaccount2
+splitaccount2 - required - the GUID for the 2nd account on this transaction
+
+GET request to /transactions/<guid> - returns an individual transaction 
+POST request to /transactions/<guid> - edits an existing transaction (accepts
+the same post variables as /transactions)
+DELETE request to /transactions/<guid> - deletes a transaction
+
+GET request to /bills - returns a list of bills
+
+GET variables:
+is_paid - optional - 1 returns paid bills, 0 unpaid
+is_active  - optional - 1 returns active bills, 0 inactive
+date_opened_from - optional - all transactions on and after a date formatted
+as YYYY-MM-DD
+date_opened_to - optional - all transactions up to and before a date formatted
+as YYYY-MM-DD
+
+POST request to /bills - creates a new bill
+
+POST variables: 
+id - optional - if no id is specified the next available sequential id will be
+used
+vendor_id - required - the id of the vendor of this bill (can be obtained from
+/vendors)
+currency - required - a gnucash currency code
+date_opened - required - a date formatted as YYYY-MM-DD
+notes - optional - notes for this bill
+
+GET request to /bills/<id> - returns an individual bill
+POST request to /bills/<id> - edits (and optionally posts) a bill
+
+POST variables:
+vendor_id - required - the id of the vendor of this bill (can be obtained from
+/vendors)
+currency - required - a gnucash currency code
+date_opened - a date formatted as YYYY-MM-DD
+notes - optional -  notes for this invoice
+posted - optional - set to 1 if marking the invoice as posted (0 or empty
+otherwise)
+posted_account_guid - required if posted = 1 - the GUID for the posted account
+posted_date - required if posted = 1 - a date formatted as YYYY-MM-DD
+due_date - required if posted = 1 - a date formatted as YYYY-MM-DD
+posted_memo - optional - a description
+posted_accumulatesplits - optional - whether to accumulate splits or not (1 for
+yes, 0 for no)
+posted_autopay - optional - whether to auto pay or not (1 for yes, 0 for no)
+
+PAY request to /bills/<id> - mark a bill as paid
+
+POST variables:
+
+posted_account_guid - required - the GUID for the posted account
+transfer_account_guid - required - the GUID for the transfer account
+payment_date - required - a date formatted as YYYY-MM-DD
+num - optional - a number for this payment
+memo - optional - a description for this payment
+auto_pay - required - whether to auto pay or not (1 for yes, 0 for no)
+
+GET request to /bills/<id>/entries  - return all entries for an individual bill
+POST request to /bills/<id>/entries - adds a new entry to a bill
+
+date - required -  a date formatted as YYYY-MM-DD
+description  - required -   the description for this entry
+account_guid  - required -  the GUID for the required accoumt
+quantity - required - the quantity
+price - required -  the price
+
+GET request to /invoices - returns all invoices (accepts the same get variables
+as /bills)
+POST request to /invoices - creates a new invoice
+
+POST variables:
+id - optional - if no id is specified the next available sequential id will be
+used
+customer_id - required - the id of the customer of this invoice (can be
+obtained from /customers)
+currency - required - a gnucash currency code
+date_opened - a date formatted as YYYY-MM-DD
+notes - optional -  notes for this invoice
+
+GET request to /invoices/<id> - returns an individual invoice
+POST request to /invoices/<id> - edits (and optionally posts) an invoice
+
+POST variables:
+customer_id - required - the id of the customer of this bill (can be obtained
+from /customers)
+currency - required - a gnucash currency code
+date_opened - a date formatted as YYYY-MM-DD
+notes - optional -  notes for this invoice
+posted - optional - set to 1 if marking the invoice as posted (0 or empty
+otherwise)
+posted_account_guid - required if posted = 1 - the GUID for the posted account
+posted_date - required if posted = 1 - a date formatted as YYYY-MM-DD
+due_date - required if posted = 1 - a date formatted as YYYY-MM-DD
+posted_memo - optional - a description
+posted_accumulatesplits - optional - whether to accumulate splits or not (1 for
+yes, 0 for no)
+posted_autopay - optional - whether to auto pay or not (1 for yes, 0 for no)
+
+PAY request to /invoices/<id> - mark an invoice as paid (accepts the same post
+variables as /bills/<id>)
+
+GET request to /invoices/<id>/entries - return all entries for an individual
+invoice
+POST request to /invoices/<id>/entries - adds a new entry to an  invoice
+(accepts the same post variables as /bills/<id>/entries)
+
+GET request to /entries/<guid> - returns an individual entry 
+POST request to /entries/<guid> - edits an existing entry (accepts the same
+post variables as /bills/<id>/entries)
+
+GET request to /customers - returns a list of customers
+POST request to /customers - creates a new customer
+
+POST variables:
+id - optional - if no id is specified the next available sequential id will be
+used
+currency - required - a gnucash currency code
+name - required - the customer's name
+contact - optional - a contact for this customer
+address_line_1 - required - the first line of the customer's address
+address_line_2 - optional - the second line of the customer's address
+address_line_3 - optional - the third line of the customer's address
+address_line_4 - optional - the fourth line of the customer's address
+phone - optional - the customer's phone number
+fax - optional - the customer's fax number
+email - optional - the customer's email address
+
+GET request to /customers/<id> - returns an individual customer
+POST request to /customers/<id> - edits an existing customer (accepts the same
+post variables as /customers)
+GET request to /customers/<id>/invoices - returns all invoices for a single
+customer
+
+GET request to /vendors - returns a list of vendors
+POST request to /vendors - creates a new vendor
+
+POST variables:
+id - optional - if no id is specified the next available sequential id will be
+used
+currency - required - a gnucash currency code
+name - required - the vendor's name
+contact - optional - a contact for this vendor
+address_line_1 - required - the first line of the vendor's address
+address_line_2 - optional - the second line of the vendor's address
+address_line_3 - optional - the third line of the vendor's address
+address_line_4 - optional - the fourth line of the vendor's address
+phone - optional - the vendor's phone number
+fax - optional - the vendor's fax number
+email - optional - the vendor's email address
+
+GET request to /vendors/<id> - returns an individual vendor
+GET request to /vendors/<id>/bills - returns all bills for a single vendor
 
 Usage
 -----
 
-**As this is work in progress you'll need a knowledge of building Gnucash from source and experience with python.**
+**As this is work in progress you'll need a knowledge of building Gnucash from
+source and experience with python.**
 
-Full details to be provided at a later date, but in short you'll need a copy of Gnucash built with Python bindings and the Flask module installed.
+Full details to be provided at a later date, but in short you'll need a copy of
+Gnucash built with Python bindings and the Flask module installed.
 
-Rrun `python ./gnucash_rest <connection string>` to start the server on <http://localhost:5000> e.g `python ./gnucash_rest mysql://user:password@127.0.0.1/gnucash_test`
+Rrun `python ./gnucash_rest <connection string>` to start the server on
+<http://localhost:5000> e.g
+`python ./gnucash_rest mysql://user:password@127.0.0.1/gnucash_test`
 
-Navigate to <http://localhost:5000/invoices> and you should get your invoices in JSON format.
+Navigate to <http://localhost:5000/invoices> and you should get your invoices
+in JSON format.
 
 Files
 -----
 
-`gnucash_rest.py` - The Flask app which responds to REST requests with JSON responses
+`gnucash_rest.py` - The Flask app which responds to REST requests with JSON
+responses
 
-`gnucash_simple.py` - A helper file to convert Gnucash objects into dictionaries for easier conversion to JSON.
+`gnucash_simple.py` - A helper file to convert Gnucash objects into
+dictionaries for easier conversion to JSON.
 
 Future
 ------
 
-I'm using the API already to integrate Gnucash invoices with other systems, so I'll be adding features as and when I need them. The format of the API is likely to change as development continues.
+I'm using the API already to integrate Gnucash invoices with other systems, so
+I'll be adding features as and when I need them. The format of the API is
+likely to change as development continues.
diff --git a/src/optional/python-bindings/example_scripts/rest-api/gnucash_rest.py b/src/optional/python-bindings/example_scripts/rest-api/gnucash_rest.py
old mode 100755
new mode 100644
index 2e21586..f6a4dc9
--- a/src/optional/python-bindings/example_scripts/rest-api/gnucash_rest.py
+++ b/src/optional/python-bindings/example_scripts/rest-api/gnucash_rest.py
@@ -1,13 +1,48 @@
 #!/usr/bin/python
 
+'''
+
+gnucash_rest.py -- A Flask app which responds to REST requests
+with JSON responses
+
+Copyright (C) 2013 Tom Lofts <dev at loftx.co.uk>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of
+the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, contact:
+
+Free Software Foundation Voice: +1-617-542-5942
+51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
+Boston, MA 02110-1301, USA gnu at gnu.org
+
+ at author Tom Lofts <dev at loftx.co.uk>
+
+'''
+
 import gnucash
 import gnucash_simple
 import json
 import atexit
-from flask import Flask, abort, request
+from flask import Flask, abort, request, Response
 import sys
 import getopt
 
+from decimal import Decimal
+
+from gnucash.gnucash_business import Vendor, Bill, Entry, GncNumeric, \
+    Customer, Invoice, Split, Account, Transaction
+
+import datetime
+
 from gnucash import \
     QOF_QUERY_AND, \
     QOF_QUERY_OR, \
@@ -36,185 +71,1842 @@ from gnucash import \
 app = Flask(__name__)
 app.debug = True
 
- at app.route('/accounts')
+ at app.route('/accounts', methods=['GET', 'POST'])
 def api_accounts():
 
-	accounts = getAccounts(session.book)
+    if request.method == 'GET':
 
-	return accounts
+        accounts = getAccounts(session.book)
 
- at app.route('/accounts/<guid>')
-def api_account(guid):
+        return Response(json.dumps(accounts), mimetype='application/json')
+
+    elif request.method == 'POST':
 
-	account = getAccount(session.book, guid)
+        try:
+            account = addAccount(session.books)
+        except Error as error:
+            return Response(json.dumps({'errors': [{'type' : error.type,
+                'message': error.message, 'data': error.data}]}), status=400,
+                mimetype='application/json')
+        else:
+            return Response(json.dumps(account), status=201,
+                mimetype='application/json')
 
-	if account is None:
-		abort(404)
-	else:
-		return account
+    else:
+        abort(405)
 
- at app.route('/invoices')
+ at app.route('/accounts/<guid>', methods=['GET'])
+def api_account(guid):
+
+    account = getAccount(session.book, guid)
+    
+    if account is None:
+        abort(404)
+    else:
+        return Response(json.dumps(account), mimetype='application/json')
+
+ at app.route('/accounts/<guid>/splits', methods=['GET'])
+def api_account_splits(guid):
+
+    date_posted_from = request.args.get('date_posted_from', None)
+    date_posted_to = request.args.get('date_posted_to', None)
+
+    # check account exists
+    account = getAccount(session.book, guid)
+
+    if account is None:
+        abort(404)
+
+    splits = getAccountSplits(session.book, guid, date_posted_from,
+        date_posted_to)
+    
+    return Response(json.dumps(splits), mimetype='application/json')
+
+
+ at app.route('/transactions', methods=['POST'])
+def api_transactions():
+
+    if request.method == 'POST':
+        
+        currency = str(request.form.get('currency', ''))
+        description = str(request.form.get('description', ''))
+        num = str(request.form.get('num', ''))
+        date_posted = str(request.form.get('date_posted', ''))
+
+        splitvalue1 = int(request.form.get('splitvalue1', ''))
+        splitaccount1 = str(request.form.get('splitaccount1', ''))
+        splitvalue2 = int(request.form.get('splitvalue2', ''))
+        splitaccount2 = str(request.form.get('splitaccount2', ''))
+
+        splits = [
+            {'value': splitvalue1, 'account_guid': splitaccount1},
+            {'value': splitvalue2, 'account_guid': splitaccount2}]
+
+        try:
+            transaction = addTransaction(session.book, num, description,
+                date_posted, currency, splits)
+        except Error as error:
+            return Response(json.dumps({'errors': [{'type' : error.type,
+                'message': error.message, 'data': error.data}]}), status=400,
+                mimetype='application/json')
+        else:
+            return Response(json.dumps(transaction), status=201,
+                mimetype='application/json')
+
+    else:
+        abort(405)
+
+ at app.route('/transactions/<guid>', methods=['GET', 'POST', 'DELETE'])
+def api_transaction(guid):
+
+    if request.method == 'GET':
+
+        transaction = getTransaction(session.book, guid)
+
+        if transaction is None:
+            abort(404)
+        
+        return Response(json.dumps(transaction), mimetype='application/json')
+
+    elif request.method == 'POST':
+
+        currency = str(request.form.get('currency', ''))
+        description = str(request.form.get('description', ''))
+        num = str(request.form.get('num', ''))
+        date_posted = str(request.form.get('date_posted', ''))
+
+        splitguid1 = str(request.form.get('splitguid1', ''))
+        splitvalue1 = int(request.form.get('splitvalue1', ''))
+        splitaccount1 = str(request.form.get('splitaccount1', ''))
+        splitguid2 = str(request.form.get('splitguid2', ''))
+        splitvalue2 = int(request.form.get('splitvalue2', ''))
+        splitaccount2 = str(request.form.get('splitaccount2', ''))
+
+        splits = [
+            {'guid': splitguid1,
+            'value': splitvalue1,
+            'account_guid': splitaccount1},
+            {'guid': splitguid2,
+            'value': splitvalue2,
+            'account_guid': splitaccount2}
+        ]
+
+        try:
+            transaction = editTransaction(session.book, guid, num, description,
+                date_posted, currency, splits)
+        except Error as error:
+            return Response(json.dumps({'errors': [{'type' : error.type,
+                'message': error.message, 'data': error.data}]}), status=400, mimetype='application/json')
+        else:
+            return Response(json.dumps(transaction), status=200,
+                mimetype='application/json')
+
+    elif request.method == 'DELETE':
+
+        deleteTransaction(session.book, guid)
+
+        return Response('', status=200, mimetype='application/json')
+
+    else:
+        abort(405)
+
+ at app.route('/bills', methods=['GET', 'POST'])
+def api_bills():
+
+    if request.method == 'GET':
+        
+        is_paid = request.args.get('is_paid', None)
+        is_active = request.args.get('is_active', None)
+        date_opened_to = request.args.get('date_opened_to', None)
+        date_opened_from = request.args.get('date_opened_from', None)
+
+        if is_paid == '1':
+            is_paid = 1
+        elif is_paid == '0':
+            is_paid = 0
+        else:
+            is_paid = None
+
+        if is_active == '1':
+            is_active = 1
+        elif is_active == '0':
+            is_active = 0
+        else:
+            is_active = None
+
+        bills = getBills(session.book, None, is_paid, is_active,
+            date_opened_from, date_opened_to)
+
+        return Response(json.dumps(bills), mimetype='application/json')
+
+    elif request.method == 'POST':
+
+        id = str(request.form.get('id', None))
+
+        if id == '':
+            id = None
+        elif id != None:
+            id = str(id)
+
+        vendor_id = str(request.form.get('vendor_id', ''))
+        currency = str(request.form.get('currency', ''))
+        date_opened = str(request.form.get('date_opened', ''))
+        notes = str(request.form.get('notes', ''))
+
+        try:
+            bill = addBill(session.book, id, vendor_id, currency, date_opened,
+                notes)
+        except Error as error:
+            # handle incorrect parameter errors
+            return Response(json.dumps({'errors': [{'type' : error.type,
+                'message': error.message, 'data': error.data}]}), status=400, mimetype='application/json')
+        else:
+            return Response(json.dumps(bill), status=201,
+                mimetype='application/json')
+
+    else:
+        abort(405)
+
+ at app.route('/bills/<id>', methods=['GET', 'POST', 'PAY'])
+def api_bill(id):
+
+    if request.method == 'GET':
+
+        bill = getBill(session.book, id)
+        
+        if bill is None:
+            abort(404)
+        else:
+            return Response(json.dumps(bill), mimetype='application/json')
+
+    elif request.method == 'POST':
+
+        vendor_id = str(request.form.get('vendor_id', ''))
+        currency = str(request.form.get('currency', ''))
+        date_opened = request.form.get('date_opened', None)
+        notes = str(request.form.get('notes', ''))
+        posted = request.form.get('posted', None)
+        posted_account_guid = str(request.form.get('posted_account_guid', ''))
+        posted_date = request.form.get('posted_date', '')
+        due_date = request.form.get('due_date', '')
+        posted_memo = str(request.form.get('posted_memo', ''))
+        posted_accumulatesplits = request.form.get('posted_accumulatesplits',
+            '')
+        posted_autopay = request.form.get('posted_autopay', '')
+
+        if posted == '1':
+            posted = 1
+        else:
+            posted = 0
+
+        if (posted_accumulatesplits == '1'
+            or posted_accumulatesplits == 'true'
+            or posted_accumulatesplits == 'True'
+            or posted_accumulatesplits == True):
+            posted_accumulatesplits = True
+        else:
+            posted_accumulatesplits = False
+
+        if posted_autopay == '1':
+            posted_autopay = True
+        else:
+            posted_autopay = False
+        try:
+            bill = updateBill(session.book, id, vendor_id, currency,
+                date_opened, notes, posted, posted_account_guid, posted_date,
+                due_date, posted_memo, posted_accumulatesplits, posted_autopay)
+        except Error as error:
+            return Response(json.dumps({'errors': [{'type' : error.type,
+                'message': error.message, 'data': error.data}]}), status=400,
+                mimetype='application/json')
+        else:
+            return Response(json.dumps(bill), status=200,
+                mimetype='application/json')
+
+        if bill is None:
+            abort(404)
+        else:
+            return Response(json.dumps(bill),
+                mimetype='application/json')
+
+    elif request.method == 'PAY':
+        
+        posted_account_guid = str(request.form.get('posted_account_guid', ''))
+        transfer_account_guid = str(request.form.get('transfer_account_guid',
+            ''))
+        payment_date = request.form.get('payment_date', '')
+        num = str(request.form.get('num', ''))
+        memo = str(request.form.get('posted_memo', ''))
+        auto_pay = request.form.get('auto_pay', '')
+
+        try:
+            bill = payBill(session.book, id, posted_account_guid,
+                transfer_account_guid, payment_date, memo, num, auto_pay)
+        except Error as error:
+            return Response(json.dumps({'errors': [{'type' : error.type,
+                'message': error.message, 'data': error.data}]}), status=400,
+            mimetype='application/json')
+        else:
+            return Response(json.dumps(bill), status=200,
+                mimetype='application/json')
+
+    else:
+        abort(405)
+
+ at app.route('/bills/<id>/entries', methods=['GET', 'POST'])
+def api_bill_entries(id):
+
+    bill = getBill(session.book, id)
+    
+    if bill is None:
+        abort(404)
+    else:
+        if request.method == 'GET':
+            return Response(json.dumps(bill['entries']), mimetype='application/json')
+        elif request.method == 'POST':
+
+            date = str(request.form.get('date', ''))
+            description = str(request.form.get('description', ''))
+            account_guid = str(request.form.get('account_guid', ''))
+            quantity = str(request.form.get('quantity', ''))
+            price = str(request.form.get('price', ''))
+
+            try:
+                entry = addBillEntry(session.book, id, date, description,
+                    account_guid, quantity, price)
+            except Error as error:
+                return Response(json.dumps({'errors': [{'type' : error.type,
+                    'message': error.message, 'data': error.data}]}),
+                    status=400, mimetype='application/json')
+            else:
+                return Response(json.dumps(entry), status=201,
+                    mimetype='application/json')
+
+        else:
+            abort(405)
+
+ at app.route('/invoices', methods=['GET', 'POST'])
 def api_invoices():
 
-	is_paid = request.args.get('is_paid', None)
-	is_active = request.args.get('is_active', None)
+    if request.method == 'GET':
+        
+        is_paid = request.args.get('is_paid', None)
+        is_active = request.args.get('is_active', None)
+        date_due_to = request.args.get('date_due_to', None)
+        date_due_from = request.args.get('date_due_from', None)
+
+        if is_paid == '1':
+            is_paid = 1
+        elif is_paid == '0':
+            is_paid = 0
+        else:
+            is_paid = None
+
+        if is_active == '1':
+            is_active = 1
+        elif is_active == '0':
+            is_active = 0
+        else:
+            is_active = None
+
+        invoices = getInvoices(session.book, None, is_paid, is_active,
+            date_due_from, date_due_to)
+
+        return Response(json.dumps(invoices), mimetype='application/json')
+
+    elif request.method == 'POST':
+
+        id = str(request.form.get('id', None))
+
+        if id == '':
+            id = None
+        elif id != None:
+            id = str(id)
+
+        customer_id = str(request.form.get('customer_id', ''))
+        currency = str(request.form.get('currency', ''))
+        date_opened = str(request.form.get('date_opened', ''))
+        notes = str(request.form.get('notes', ''))
+
+        try:
+            invoice = addInvoice(session.book, id, customer_id, currency,
+                date_opened, notes)
+        except Error as error:
+            return Response(json.dumps({'errors': [{'type' : error.type,
+                'message': error.message, 'data': error.data}]}), status=400,
+                mimetype='application/json')
+        else:
+            return Response(json.dumps(invoice), status=201,
+                mimetype='application/json')
+
+    else:
+        abort(405)
+
+ at app.route('/invoices/<id>', methods=['GET', 'POST', 'PAY'])
+def api_invoice(id):
 
-	if is_paid == '1':
-		is_paid = 1
-	elif is_paid == '0':
-		is_paid = 0
-	else:
-		is_paid = None
+    if request.method == 'GET':
+
+        invoice = getInvoice(session.book, id)
+        
+        if invoice is None:
+            abort(404)
+        else:
+            return Response(json.dumps(invoice), mimetype='application/json')
+
+    elif request.method == 'POST':
+
+        customer_id = str(request.form.get('customer_id', ''))
+        currency = str(request.form.get('currency', ''))
+        date_opened = request.form.get('date_opened', None)
+        notes = str(request.form.get('notes', ''))
+        posted = request.form.get('posted', None)
+        posted_account_guid = str(request.form.get('posted_account_guid', ''))
+        posted_date = request.form.get('posted_date', '')
+        due_date = request.form.get('due_date', '')
+        posted_memo = str(request.form.get('posted_memo', ''))
+        posted_accumulatesplits = request.form.get('posted_accumulatesplits',
+            '')
+        posted_autopay = request.form.get('posted_autopay', '')
+
+        if posted == '1':
+            posted = 1
+        else:
+            posted = 0
+
+        if (posted_accumulatesplits == '1'
+            or posted_accumulatesplits == 'true'
+            or posted_accumulatesplits == 'True'
+            or posted_accumulatesplits == True):
+            posted_accumulatesplits = True
+        else:
+            posted_accumulatesplits = False
+
+        if posted_autopay == '1':
+            posted_autopay = True
+        else:
+            posted_autopay = False
+        try:
+            invoice = updateInvoice(session.book, id, customer_id, currency,
+                date_opened, notes, posted, posted_account_guid, posted_date,
+                due_date, posted_memo, posted_accumulatesplits, posted_autopay)
+        except Error as error:
+            return Response(json.dumps({'errors': [{'type' : error.type,
+                'message': error.message, 'data': error.data}]}), status=400,
+                mimetype='application/json')
+        else:
+            return Response(json.dumps(invoice), status=200,
+                mimetype='application/json')
+
+        if invoice is None:
+            abort(404)
+        else:
+            return Response(json.dumps(invoice), mimetype='application/json')
+
+    elif request.method == 'PAY':
+        
+        posted_account_guid = str(request.form.get('posted_account_guid', ''))
+        transfer_account_guid = str(request.form.get('transfer_account_guid',
+            ''))
+        payment_date = request.form.get('payment_date', '')
+        num = str(request.form.get('num', ''))
+        memo = str(request.form.get('posted_memo', ''))
+        auto_pay = request.form.get('auto_pay', '')
+
+        try:
+            invoice = payInvoice(session.book, id, posted_account_guid,
+                transfer_account_guid, payment_date, memo, num, auto_pay)
+        except Error as error:
+            return Response(json.dumps({'errors': [{'type' : error.type,
+                'message': error.message, 'data': error.data}]}), status=400,
+            mimetype='application/json')
+        else:
+            return Response(json.dumps(invoice), status=200,
+                mimetype='application/json')
+
+    else:
+        abort(405)
+
+ at app.route('/invoices/<id>/entries', methods=['GET', 'POST'])
+def api_invoice_entries(id):
+
+    invoice = getInvoice(session.book, id)
+    
+    if invoice is None:
+        abort(404)
+    else:
+        if request.method == 'GET':
+            return Response(json.dumps(invoice['entries']),
+                mimetype='application/json')
+        elif request.method == 'POST':
+
+            date = str(request.form.get('date', ''))
+            description = str(request.form.get('description', ''))
+            account_guid = str(request.form.get('account_guid', ''))
+            quantity = str(request.form.get('quantity', ''))
+            price = str(request.form.get('price', ''))
+
+            try:
+                entry = addEntry(session.book, id, date, description,
+                    account_guid, quantity, price)
+            except Error as error:
+                return Response(json.dumps({'errors': [{'type' : error.type,
+                    'message': error.message, 'data': error.data}]}),
+                    status=400, mimetype='application/json')
+            else:
+                return Response(json.dumps(entry), status=201,
+                    mimetype='application/json')
+
+        else:
+            abort(405)
+
+ at app.route('/entries/<guid>', methods=['GET', 'POST', 'DELETE'])
+def api_entry(guid):
+
+    entry = getEntry(session.book, guid)
+    
+    if entry is None:
+        abort(404)
+    else:
+        if request.method == 'GET':
+            return Response(json.dumps(entry), mimetype='application/json')
+        elif request.method == 'POST':
+
+            date = str(request.form.get('date', ''))
+            description = str(request.form.get('description', ''))
+            account_guid = str(request.form.get('account_guid', ''))
+            quantity = str(request.form.get('quantity', ''))
+            price = str(request.form.get('price', ''))
+
+            try:
+                entry = updateEntry(session.book, guid, date, description,
+                    account_guid, quantity, price)
+            except Error as error:
+                return Response(json.dumps({'errors': [{'type' : error.type,
+                    'message': error.message, 'data': error.data}]}),
+                    status=400, mimetype='application/json')
+            else:
+                return Response(json.dumps(entry), status=200,
+                    mimetype='application/json')
+
+        elif request.method == 'DELETE':
+
+            deleteEntry(session.book, guid)
+
+            return Response('', status=201, mimetype='application/json')
+
+        else:
+            abort(405)
+
+ at app.route('/customers', methods=['GET', 'POST'])
+def api_customers(): 
+
+    if request.method == 'GET':
+        customers = getCustomers(session.book)
+        return Response(json.dumps(customers), mimetype='application/json')
+    elif request.method == 'POST':
+
+        id = str(request.form.get('id', None))
+
+        if id == '':
+            id = None
+        elif id != None:
+            id = str(id)
+
+        currency = str(request.form.get('currency', ''))
+        name = str(request.form.get('name', ''))
+        contact = str(request.form.get('contact', ''))
+        address_line_1 = str(request.form.get('address_line_1', ''))
+        address_line_2 = str(request.form.get('address_line_2', ''))
+        address_line_3 = str(request.form.get('address_line_3', ''))
+        address_line_4 = str(request.form.get('address_line_4', ''))
+        phone = str(request.form.get('phone', ''))
+        fax = str(request.form.get('fax', ''))
+        email = str(request.form.get('email', ''))
+
+        try:
+            customer = addCustomer(session.book, id, currency, name, contact,
+                address_line_1, address_line_2, address_line_3, address_line_4,
+                phone, fax, email)
+        except Error as error:
+            return Response(json.dumps({'errors': [{'type' : error.type,
+                'message': error.message, 'data': error.data}]}), status=400,
+                mimetype='application/json')
+        else:
+            return Response(json.dumps(customer), status=201,
+                mimetype='application/json')
+
+    else:
+        abort(405)
+
+ at app.route('/customers/<id>', methods=['GET', 'POST'])
+def api_customer(id):
 
-	if is_active == '1':
-		is_active = 1
-	elif is_active == '0':
-		is_active = 0
-	else:
-		is_active = None
+    if request.method == 'GET':
+
+        customer = getCustomer(session.book, id)
+
+        if customer is None:
+            abort(404)
+        else:
+            return Response(json.dumps(customer), mimetype='application/json')
+
+    elif request.method == 'POST':
+
+        id = str(request.form.get('id', None))
+
+        name = str(request.form.get('name', ''))
+        contact = str(request.form.get('contact', ''))
+        address_line_1 = str(request.form.get('address_line_1', ''))
+        address_line_2 = str(request.form.get('address_line_2', ''))
+        address_line_3 = str(request.form.get('address_line_3', ''))
+        address_line_4 = str(request.form.get('address_line_4', ''))
+        phone = str(request.form.get('phone', ''))
+        fax = str(request.form.get('fax', ''))
+        email = str(request.form.get('email', ''))
+
+        try:
+            customer = updateCustomer(session.book, id, name, contact,
+                address_line_1, address_line_2, address_line_3, address_line_4,
+                phone, fax, email)
+        except Error as error:
+            if error.type == 'NoCustomer':
+                return Response(json.dumps({'errors': [{'type' : error.type,
+                    'message': error.message, 'data': error.data}]}),
+                    status=404, mimetype='application/json')
+            else:
+                return Response(json.dumps({'errors': [{'type' : error.type,
+                    'message': error.message, 'data': error.data}]}),
+                    status=400, mimetype='application/json')
+        else:
+            return Response(json.dumps(customer), status=200,
+                mimetype='application/json')
+
+    else:
+        abort(405)
+
+ at app.route('/customers/<id>/invoices', methods=['GET'])
+def api_customer_invoices(id):
+
+    customer = getCustomer(session.book, id)
+    
+    if customer is None:
+        abort(404)
+    
+    invoices = getInvoices(session.book, customer['guid'], None, None, None,
+        None)
+    
+    return Response(json.dumps(invoices), mimetype='application/json')
+
+ at app.route('/vendors', methods=['GET', 'POST'])
+def api_vendors(): 
+
+    if request.method == 'GET':
+        vendors = getVendors(session.book)
+        return Response(json.dumps(vendors), mimetype='application/json')
+    elif request.method == 'POST':
+
+        id = str(request.form.get('id', None))
+
+        if id == '':
+            id = None
+        elif id != None:
+            id = str(id)
+
+        currency = str(request.form.get('currency', ''))
+        name = str(request.form.get('name', ''))
+        contact = str(request.form.get('contact', ''))
+        address_line_1 = str(request.form.get('address_line_1', ''))
+        address_line_2 = str(request.form.get('address_line_2', ''))
+        address_line_3 = str(request.form.get('address_line_3', ''))
+        address_line_4 = str(request.form.get('address_line_4', ''))
+        phone = str(request.form.get('phone', ''))
+        fax = str(request.form.get('fax', ''))
+        email = str(request.form.get('email', ''))
+
+        try:
+            vendor = addVendor(session.book, id, currency, name, contact,
+                address_line_1, address_line_2, address_line_3, address_line_4,
+                phone, fax, email)
+        except Error as error:
+            return Response(json.dumps({'errors': [{'type' : error.type,
+                'message': error.message, 'data': error.data}]}), status=400,
+                mimetype='application/json')
+        else:
+            return Response(json.dumps(vendor), status=201,
+                mimetype='application/json')
+
+    else:
+        abort(405)
+
+ at app.route('/vendors/<id>', methods=['GET', 'POST'])
+def api_vendor(id):
+
+    if request.method == 'GET':
+
+        vendor = getVendor(session.book, id)
+
+        if vendor is None:
+            abort(404)
+        else:
+            return Response(json.dumps(vendor), mimetype='application/json')
+    else:
+        abort(405)
+
+ at app.route('/vendors/<id>/bills', methods=['GET'])
+def api_vendor_bills(id):
+
+    vendor = getVendor(session.book, id)
+    
+    if vendor is None:
+        abort(404)
+    
+    bills = getBills(session.book, vendor['guid'], None, None, None, None)
+    
+    return Response(json.dumps(bills), mimetype='application/json')
 
-	invoices = getInvoices(session.book, is_paid, is_active)
+def getCustomers(book):
 
-	return invoices
+    query = gnucash.Query()
+    query.search_for('gncCustomer')
+    query.set_book(book)
+    customers = []
 
- at app.route('/invoices/<id>')
-def api_invoice(id):
+    for result in query.run():
+        customers.append(gnucash_simple.customerToDict(
+            gnucash.gnucash_business.Customer(instance=result)))
 
-	invoice = getInvoice(session.book, id)
+    query.destroy()
 
-	if invoice is None:
-		abort(404)
-	else:
-		return invoice
+    return customers
 
+def getCustomer(book, id):
 
- at app.route('/customers')
-def api_customers():
+    customer = book.CustomerLookupByID(id)
 
-	customers = getCustomers(session.book)
+    if customer is None:
+        return None
+    else:
+        return gnucash_simple.customerToDict(customer)
 
-	return customers
+def getVendors(book):
 
- at app.route('/customers/<id>')
-def api_customer(id):
+    query = gnucash.Query()
+    query.search_for('gncVendor')
+    query.set_book(book)
+    vendors = []
 
-	customer = getCustomer(session.book, id)
+    for result in query.run():
+        vendors.append(gnucash_simple.vendorToDict(
+            gnucash.gnucash_business.Vendor(instance=result)))
 
-	if customer is None:
-		abort(404)
-	else:
-		return customer
+    query.destroy()
 
-def getCustomers(book):
+    return vendors
 
-	query = gnucash.Query()
-	query.search_for('gncCustomer')
-	query.set_book(book)
-	customers = []
+def getVendor(book, id):
 
-	for result in query.run():
-		customers.append(gnucash_simple.customerToDict(gnucash.gnucash_business.Customer(instance=result)))
+    vendor = book.VendorLookupByID(id)
 
-	query.destroy()
+    if vendor is None:
+        return None
+    else:
+        return gnucash_simple.vendorToDict(vendor)
 
-	return json.dumps(customers)
+def getAccounts(book):
 
-def getCustomer(book, id):
+    accounts = gnucash_simple.accountToDict(book.get_root_account())
 
-	customer = book.CustomerLookupByID(id)
+    return accounts
 
-	if customer is None:
-		return None
-	else:
-		return json.dumps(gnucash_simple.customerToDict(customer))
+def getAccountsFlat(book):
 
-def getAccounts(book):
+    accounts = gnucash_simple.accountToDict(book.get_root_account())
+
+    flat_accounts = getSubAccounts(accounts)
+
+    for n, account in enumerate(flat_accounts):
+        account.pop('subaccounts')
+
+    filtered_flat_account = []
 
-	accounts = gnucash_simple.accountToDict(book.get_root_account())
+    type_ids = [9]
 
-	return json.dumps(accounts)
+    for n, account in enumerate(flat_accounts):
+        if account['type_id'] in type_ids:
+            filtered_flat_account.append(account)
+            print account['name'] + ' ' + str(account['type_id'])
+
+    return filtered_flat_account
+
+def getSubAccounts(account):
+
+    flat_accounts = []
+
+    if 'subaccounts' in account.keys():
+        for n, subaccount in enumerate(account['subaccounts']):
+            flat_accounts.append(subaccount)
+            flat_accounts = flat_accounts + getSubAccounts(subaccount)
+
+    return flat_accounts
 
 def getAccount(book, guid):
 
-	account_guid = gnucash.gnucash_core.GUID()
-	gnucash.gnucash_core.GUIDString(guid, account_guid)
+    account_guid = gnucash.gnucash_core.GUID() 
+    gnucash.gnucash_core.GUIDString(guid, account_guid)
+
+    account = account_guid.AccountLookup(book)
+
+    if account is None:
+        return None
+
+    account = gnucash_simple.accountToDict(account)
+
+    if account is None:
+        return None
+    else:
+        return account
+
+
+def getTransaction(book, guid):
+
+    transaction_guid = gnucash.gnucash_core.GUID() 
+    gnucash.gnucash_core.GUIDString(guid, transaction_guid)
+
+    transaction = transaction_guid.TransactionLookup(book)
+
+    if transaction is None:
+        return None
+
+    transaction = gnucash_simple.transactionToDict(transaction, ['splits'])
+
+    if transaction is None:
+        return None
+    else:
+        return transaction
+
+def getTransactions(book, account_guid, date_posted_from, date_posted_to):
+
+    query = gnucash.Query()
+
+    query.search_for('Trans')
+    query.set_book(book)
+
+    transactions = []
+
+    for transaction in query.run():
+        transactions.append(gnucash_simple.transactionToDict(
+            gnucash.gnucash_business.Transaction(instance=transaction)))
+
+    query.destroy()
+
+    return transactions
+
+def getAccountSplits(book, guid, date_posted_from, date_posted_to):
+
+    account_guid = gnucash.gnucash_core.GUID() 
+    gnucash.gnucash_core.GUIDString(guid, account_guid)
+
+    query = gnucash.Query()
+    query.search_for('Split')
+    query.set_book(book)
+
+    SPLIT_TRANS= 'trans'
+
+    QOF_DATE_MATCH_NORMAL = 1
+
+    TRANS_DATE_POSTED = 'date-posted'
 
-	account = gnucash_simple.accountToDict(account_guid.AccountLookup(book))
+    if date_posted_from != None:
+        pred_data = gnucash.gnucash_core.QueryDatePredicate(
+            QOF_COMPARE_GTE, QOF_DATE_MATCH_NORMAL, datetime.datetime.strptime(
+                date_posted_from, "%Y-%m-%d").date())
+        param_list = [SPLIT_TRANS, TRANS_DATE_POSTED]
+        query.add_term(param_list, pred_data, QOF_QUERY_AND)
 
-	if account is None:
-		return None
-	else:
-		return json.dumps(account)
+    if date_posted_to != None:
+        pred_data = gnucash.gnucash_core.QueryDatePredicate(
+            QOF_COMPARE_LTE, QOF_DATE_MATCH_NORMAL, datetime.datetime.strptime(
+                date_posted_to, "%Y-%m-%d").date())
+        param_list = [SPLIT_TRANS, TRANS_DATE_POSTED]
+        query.add_term(param_list, pred_data, QOF_QUERY_AND)
+    
+    SPLIT_ACCOUNT = 'account'
+    QOF_PARAM_GUID = 'guid'
 
+    if guid != None:
+        gnucash.gnucash_core.GUIDString(guid, account_guid)
+        query.add_guid_match(
+            [SPLIT_ACCOUNT, QOF_PARAM_GUID], account_guid, QOF_QUERY_AND)
 
-def getInvoices(book, is_paid, is_active):
+    splits = []
 
-	query = gnucash.Query()
-	query.search_for('gncInvoice')
-	query.set_book(book)
+    for split in query.run():
+        splits.append(gnucash_simple.splitToDict(
+            gnucash.gnucash_business.Split(instance=split),
+            ['account', 'transaction', 'other_split']))
 
-	if is_paid == 0:
-		query.add_boolean_match([INVOICE_IS_PAID], False, QOF_QUERY_AND)
-	elif is_paid == 1:
-		query.add_boolean_match([INVOICE_IS_PAID], True, QOF_QUERY_AND)
+    query.destroy()
 
-	# active = JOB_IS_ACTIVE
-	if is_active == 0:
-		query.add_boolean_match(['active'], False, QOF_QUERY_AND)
-	elif is_active == 1:
-		query.add_boolean_match(['active'], True, QOF_QUERY_AND)
+    return splits
 
-	# return only invoices (1 = invoices)
-	pred_data = gnucash.gnucash_core.QueryInt32Predicate(QOF_COMPARE_EQUAL, 1)
-	query.add_term([INVOICE_TYPE], pred_data, QOF_QUERY_AND)
+def getInvoices(book, customer, is_paid, is_active, date_due_from,
+    date_due_to):
 
-	invoices = []
+    query = gnucash.Query()
+    query.search_for('gncInvoice')
+    query.set_book(book)
 
-	for result in query.run():
-		invoices.append(gnucash_simple.invoiceToDict(gnucash.gnucash_business.Invoice(instance=result)))
+    if is_paid == 0:
+        query.add_boolean_match([INVOICE_IS_PAID], False, QOF_QUERY_AND)
+    elif is_paid == 1:
+        query.add_boolean_match([INVOICE_IS_PAID], True, QOF_QUERY_AND)
 
-	query.destroy()
+    # active = JOB_IS_ACTIVE
+    if is_active == 0:
+        query.add_boolean_match(['active'], False, QOF_QUERY_AND)
+    elif is_active == 1:
+        query.add_boolean_match(['active'], True, QOF_QUERY_AND)
 
-	return json.dumps(invoices)
+    QOF_PARAM_GUID = 'guid'
+    INVOICE_OWNER = 'owner'
+
+    if customer != None:
+        customer_guid = gnucash.gnucash_core.GUID() 
+        gnucash.gnucash_core.GUIDString(customer, customer_guid)
+        query.add_guid_match(
+            [INVOICE_OWNER, QOF_PARAM_GUID], customer_guid, QOF_QUERY_AND)
+
+    if date_due_from != None:
+        pred_data = gnucash.gnucash_core.QueryDatePredicate(
+            QOF_COMPARE_GTE, 2, datetime.datetime.strptime(
+                date_due_from, "%Y-%m-%d").date())
+        query.add_term(['date_due'], pred_data, QOF_QUERY_AND)
+
+    if date_due_to != None:
+        pred_data = gnucash.gnucash_core.QueryDatePredicate(
+            QOF_COMPARE_LTE, 2, datetime.datetime.strptime(
+                date_due_to, "%Y-%m-%d").date())
+        query.add_term(['date_due'], pred_data, QOF_QUERY_AND)
+
+    # return only invoices (1 = invoices)
+    pred_data = gnucash.gnucash_core.QueryInt32Predicate(QOF_COMPARE_EQUAL, 1)
+    query.add_term([INVOICE_TYPE], pred_data, QOF_QUERY_AND)
+
+    invoices = []
+
+    for result in query.run():
+        invoices.append(gnucash_simple.invoiceToDict(
+            gnucash.gnucash_business.Invoice(instance=result)))
+
+    query.destroy()
+
+    return invoices
+
+def getBills(book, customer, is_paid, is_active, date_opened_from,
+    date_opened_to):
+
+    query = gnucash.Query()
+    query.search_for('gncInvoice')
+    query.set_book(book)
+
+    if is_paid == 0:
+        query.add_boolean_match([INVOICE_IS_PAID], False, QOF_QUERY_AND)
+    elif is_paid == 1:
+        query.add_boolean_match([INVOICE_IS_PAID], True, QOF_QUERY_AND)
+
+    # active = JOB_IS_ACTIVE
+    if is_active == 0:
+        query.add_boolean_match(['active'], False, QOF_QUERY_AND)
+    elif is_active == 1:
+        query.add_boolean_match(['active'], True, QOF_QUERY_AND)
+
+    QOF_PARAM_GUID = 'guid'
+    INVOICE_OWNER = 'owner'
+
+    if customer != None:
+        customer_guid = gnucash.gnucash_core.GUID() 
+        gnucash.gnucash_core.GUIDString(customer, customer_guid)
+        query.add_guid_match(
+            [INVOICE_OWNER, QOF_PARAM_GUID], customer_guid, QOF_QUERY_AND)
+
+    if date_opened_from != None:
+        pred_data = gnucash.gnucash_core.QueryDatePredicate(
+            QOF_COMPARE_GTE, 2, datetime.datetime.strptime(
+                date_opened_from, "%Y-%m-%d").date())
+        query.add_term(['date_opened'], pred_data, QOF_QUERY_AND)
+
+    if date_opened_to != None:
+        pred_data = gnucash.gnucash_core.QueryDatePredicate(
+            QOF_COMPARE_LTE, 2, datetime.datetime.strptime(
+                date_opened_to, "%Y-%m-%d").date())
+        query.add_term(['date_opened'], pred_data, QOF_QUERY_AND)
+
+    # return only bills (2 = bills)
+    pred_data = gnucash.gnucash_core.QueryInt32Predicate(QOF_COMPARE_EQUAL, 2)
+    query.add_term([INVOICE_TYPE], pred_data, QOF_QUERY_AND)
+
+    bills = []
+
+    for result in query.run():
+        bills.append(gnucash_simple.billToDict(
+            gnucash.gnucash_business.Bill(instance=result)))
+
+    query.destroy()
+
+    return bills
+
+def getGnuCashInvoice(book ,id):
+
+    # we don't use book.InvoicelLookupByID(id) as this is identical to
+    # book.BillLookupByID(id) so can return the same object if they share IDs
+
+    query = gnucash.Query()
+    query.search_for('gncInvoice')
+    query.set_book(book)
+
+    # return only invoices (1 = invoices)
+    pred_data = gnucash.gnucash_core.QueryInt32Predicate(QOF_COMPARE_EQUAL, 1)
+    query.add_term([INVOICE_TYPE], pred_data, QOF_QUERY_AND)
+
+    INVOICE_ID = 'id'
+
+    pred_data = gnucash.gnucash_core.QueryStringPredicate(
+        QOF_COMPARE_EQUAL, id, QOF_STRING_MATCH_NORMAL, False)
+    query.add_term([INVOICE_ID], pred_data, QOF_QUERY_AND)
+
+    invoice = None
+
+    for result in query.run():
+        invoice = gnucash.gnucash_business.Invoice(instance=result)
+
+    query.destroy()
+
+    return invoice
+
+def getGnuCashBill(book ,id):
+
+    # we don't use book.InvoicelLookupByID(id) as this is identical to
+    # book.BillLookupByID(id) so can return the same object if they share IDs
+
+    query = gnucash.Query()
+    query.search_for('gncInvoice')
+    query.set_book(book)
+
+    # return only bills (2 = bills)
+    pred_data = gnucash.gnucash_core.QueryInt32Predicate(QOF_COMPARE_EQUAL, 2)
+    query.add_term([INVOICE_TYPE], pred_data, QOF_QUERY_AND)
+
+    INVOICE_ID = 'id'
+
+    pred_data = gnucash.gnucash_core.QueryStringPredicate(
+        QOF_COMPARE_EQUAL, id, QOF_STRING_MATCH_NORMAL, False)
+    query.add_term([INVOICE_ID], pred_data, QOF_QUERY_AND)
+
+    bill = None
+
+    for result in query.run():
+        bill = gnucash.gnucash_business.Bill(instance=result)
+
+    query.destroy()
+
+    return bill
 
 def getInvoice(book, id):
 
-	invoice = book.InvoiceLookupByID(id)
+    return gnucash_simple.invoiceToDict(getGnuCashInvoice(book, id))
+
+def payInvoice(book, id, posted_account_guid, transfer_account_guid,
+    payment_date, memo, num, auto_pay):
+
+    invoice = getGnuCashInvoice(book, id)
+    
+    account_guid2 = gnucash.gnucash_core.GUID() 
+    gnucash.gnucash_core.GUIDString(transfer_account_guid, account_guid2)
+
+    xfer_acc = account_guid2.AccountLookup(session.book)
+
+    invoice.ApplyPayment(None, xfer_acc, invoice.GetTotal(), GncNumeric(0),
+        datetime.datetime.strptime(payment_date, '%Y-%m-%d'), memo, num)
+
+    return gnucash_simple.invoiceToDict(invoice)    
+
+def payBill(book, id, posted_account_guid, transfer_account_guid, payment_date,
+    memo, num, auto_pay):
+
+    bill = getGnuCashBill(book, id)
+
+    account_guid = gnucash.gnucash_core.GUID() 
+    gnucash.gnucash_core.GUIDString(transfer_account_guid, account_guid)
+
+    xfer_acc = account_guid.AccountLookup(session.book)
+
+    # We pay the negitive total as the bill as this seemed to cause issues
+    # with the split not being set correctly and not being marked as paid
+    bill.ApplyPayment(None, xfer_acc, bill.GetTotal().neg(), GncNumeric(0),
+        datetime.datetime.strptime(payment_date, '%Y-%m-%d'), memo, num)
+
+    return gnucash_simple.billToDict(bill)
+
+def getBill(book, id):
+
+    return gnucash_simple.billToDict(getGnuCashBill(book, id))
+
+def addVendor(book, id, currency_mnumonic, name, contact, address_line_1,
+    address_line_2, address_line_3, address_line_4, phone, fax, email):
+
+    if name == '':
+        raise Error('NoVendorName', 'A name must be entered for this company',
+            {'field': 'name'})
+
+    if (address_line_1 == ''
+        and address_line_2 == ''
+        and address_line_3 == ''
+        and address_line_4 == ''):
+        raise Error('NoVendorAddress',
+            'An address must be entered for this company',
+            {'field': 'address'})
 
-	if invoice is None:
-		return None
-	else:
-		#print invoiceToDict(invoice)
-		return json.dumps(gnucash_simple.invoiceToDict(invoice))
+    commod_table = book.get_table()
+    currency = commod_table.lookup('CURRENCY', currency_mnumonic)
+
+    if currency is None:
+        raise Error('InvalidVendorCurrency',
+            'A valid currency must be supplied for this vendor',
+            {'field': 'currency'})
+
+    if id is None:
+        id = book.VendorNextID()
+
+    vendor = Vendor(session.book, id, currency, name)
+
+    address = vendor.GetAddr()
+    address.SetName(contact)
+    address.SetAddr1(address_line_1)
+    address.SetAddr2(address_line_2)
+    address.SetAddr3(address_line_3)
+    address.SetAddr4(address_line_4)
+    address.SetPhone(phone)
+    address.SetFax(fax)
+    address.SetEmail(email)
+
+    return gnucash_simple.vendorToDict(vendor)
+
+def addCustomer(book, id, currency_mnumonic, name, contact, address_line_1,
+    address_line_2, address_line_3, address_line_4, phone, fax, email):
+
+    if name == '':
+        raise Error('NoCustomerName',
+            'A name must be entered for this company', {'field': 'name'})
+
+    if (address_line_1 == ''
+        and address_line_2 == ''
+        and address_line_3 == ''
+        and address_line_4 == ''):
+        raise Error('NoCustomerAddress',
+            'An address must be entered for this company',
+            {'field': 'address'})
+
+    commod_table = book.get_table()
+    currency = commod_table.lookup('CURRENCY', currency_mnumonic)
+
+    if currency is None:
+        raise Error('InvalidCustomerCurrency',
+            'A valid currency must be supplied for this customer',
+            {'field': 'currency'})
+
+    if id is None:
+        id = book.CustomerNextID()
+
+    customer = Customer(session.book, id, currency, name)
+
+    address = customer.GetAddr()
+    address.SetName(contact)
+    address.SetAddr1(address_line_1)
+    address.SetAddr2(address_line_2)
+    address.SetAddr3(address_line_3)
+    address.SetAddr4(address_line_4)
+    address.SetPhone(phone)
+    address.SetFax(fax)
+    address.SetEmail(email)
+
+    return gnucash_simple.customerToDict(customer)
+
+def updateCustomer(book, id, name, contact, address_line_1, address_line_2,
+    address_line_3, address_line_4, phone, fax, email):
+
+    customer = book.CustomerLookupByID(id)
+
+    if customer is None:
+        raise Error('NoCustomer', 'A customer with this ID does not exist',
+            {'field': 'id'})
+
+    if name == '':
+        raise Error('NoCustomerName',
+            'A name must be entered for this company', {'field': 'name'})
+
+    if (address_line_1 == ''
+        and address_line_2 == ''
+        and address_line_3 == ''
+        and address_line_4 == ''):
+        raise Error('NoCustomerAddress',
+            'An address must be entered for this company',
+            {'field': 'address'})
+
+    customer.SetName(name)
+
+    address = customer.GetAddr()
+    address.SetName(contact)
+    address.SetAddr1(address_line_1)
+    address.SetAddr2(address_line_2)
+    address.SetAddr3(address_line_3)
+    address.SetAddr4(address_line_4)
+    address.SetPhone(phone)
+    address.SetFax(fax)
+    address.SetEmail(email)
+
+    return gnucash_simple.customerToDict(customer)
+
+def addInvoice(book, id, customer_id, currency_mnumonic, date_opened, notes):
+
+    customer = book.CustomerLookupByID(customer_id)
+
+    if customer is None:
+        raise Error('NoCustomer',
+            'A customer with this ID does not exist', {'field': 'id'})
+
+    if id is None:
+        id = book.InvoiceNextID(customer)
+
+    try:
+        date_opened = datetime.datetime.strptime(date_opened, "%Y-%m-%d")
+    except ValueError:
+        raise Error('InvalidDateOpened',
+            'The date opened must be provided in the form YYYY-MM-DD',
+            {'field': 'date_opened'})
+
+    if currency_mnumonic is None:
+        currency_mnumonic = customer.GetCurrency().get_mnemonic()
+
+    commod_table = book.get_table()
+    currency = commod_table.lookup('CURRENCY', currency_mnumonic)
+
+    if currency is None:
+        raise Error('InvalidCustomerCurrency',
+            'A valid currency must be supplied for this customer',
+            {'field': 'currency'})
+
+    invoice = Invoice(book, id, currency, customer, date_opened.date())
+
+    invoice.SetNotes(notes)
+
+    return gnucash_simple.invoiceToDict(invoice)
+
+def updateInvoice(book, id, customer_id, currency_mnumonic, date_opened,
+    notes, posted, posted_account_guid, posted_date, due_date, posted_memo,
+    posted_accumulatesplits, posted_autopay):
+
+    invoice = getGnuCashInvoice(book, id)
+
+    if invoice is None:
+        raise Error('NoInvoice',
+            'An invoice with this ID does not exist',
+            {'field': 'id'})
+
+    customer = book.CustomerLookupByID(customer_id)
+
+    if customer is None:
+        raise Error('NoCustomer', 'A customer with this ID does not exist',
+            {'field': 'customer_id'})
+
+    try:
+        date_opened = datetime.datetime.strptime(date_opened, "%Y-%m-%d")
+    except ValueError:
+        raise Error('InvalidDateOpened',
+            'The date opened must be provided in the form YYYY-MM-DD',
+            {'field': 'date_opened'})
+
+    if posted_date == '':
+        if posted == 1:
+            raise Error('NoDatePosted',
+                'The date posted must be supplied when posted=1',
+                {'field': 'date_posted'})
+    else:
+        try:
+            posted_date = datetime.datetime.strptime(posted_date, "%Y-%m-%d")
+        except ValueError:
+            raise Error('InvalidDatePosted',
+                'The date posted must be provided in the form YYYY-MM-DD',
+                {'field': 'posted_date'})
+
+    if due_date == '':
+        if posted == 1:
+            raise Error('NoDatePosted',
+                'The due date must be supplied when posted=1',
+                {'field': 'date_posted'})
+    else:
+        try:
+            due_date = datetime.datetime.strptime(due_date, "%Y-%m-%d")
+        except ValueError:
+            raise Error('InvalidDatePosted',
+                'The due date must be provided in the form YYYY-MM-DD',
+                {'field': 'due_date'})
+
+    if posted_account_guid == '':
+        if posted == 1:
+            raise Error('NoPostedAccountGuid',
+                'The posted account GUID must be supplied when posted=1',
+                {'field': 'posted_account_guid'})
+    else:
+        guid = gnucash.gnucash_core.GUID() 
+        gnucash.gnucash_core.GUIDString(posted_account_guid, guid)
+
+        posted_account = guid.AccountLookup(book)
+
+        if posted_account is None:
+            raise Error('NoAccount',
+                'No account exists with the posted account GUID',
+                {'field': 'posted_account_guid'})
+
+    invoice.SetOwner(customer)
+    invoice.SetDateOpened(date_opened)
+    invoice.SetNotes(notes)
+
+    # post if currently unposted and posted=1
+    if (invoice.GetDatePosted().strftime('%Y-%m-%d') == '1970-01-01'
+        and posted == 1):
+        invoice.PostToAccount(posted_account, posted_date, due_date,
+            posted_memo, posted_accumulatesplits, posted_autopay)
+
+    return gnucash_simple.invoiceToDict(invoice)
+
+def updateBill(book, id, vendor_id, currency_mnumonic, date_opened, notes,
+    posted, posted_account_guid, posted_date, due_date, posted_memo,
+    posted_accumulatesplits, posted_autopay):
+
+    bill = getGnuCashBill(book, id)
+
+    if bill is None:
+        raise Error('NoBill', 'A bill with this ID does not exist',
+            {'field': 'id'})
+
+    vendor = book.VendorLookupByID(vendor_id)
+
+    if vendor is None:
+        raise Error('NoVendor',
+            'A vendor with this ID does not exist',
+            {'field': 'vendor_id'})
+
+    try:
+        date_opened = datetime.datetime.strptime(date_opened, "%Y-%m-%d")
+    except ValueError:
+        raise Error('InvalidDateOpened',
+            'The date opened must be provided in the form YYYY-MM-DD',
+            {'field': 'date_opened'})
+
+    if posted_date == '':
+        if posted == 1:
+            raise Error('NoDatePosted',
+                'The date posted must be supplied when posted=1',
+                {'field': 'date_posted'})
+    else:
+        try:
+            posted_date = datetime.datetime.strptime(posted_date, "%Y-%m-%d")
+        except ValueError:
+            raise Error('InvalidDatePosted',
+                'The date posted must be provided in the form YYYY-MM-DD',
+                {'field': 'posted_date'})
+
+    if due_date == '':
+        if posted == 1:
+            raise Error('NoDatePosted',
+                'The due date must be supplied when posted=1',
+                {'field': 'date_posted'})
+    else:
+        try:
+            due_date = datetime.datetime.strptime(due_date, "%Y-%m-%d")
+        except ValueError:
+            raise Error('InvalidDatePosted',
+                'The due date must be provided in the form YYYY-MM-DD',
+                {'field': 'due_date'})
+
+    if posted_account_guid == '':
+        if posted == 1:
+            raise Error('NoPostedAccountGuid',
+                'The posted account GUID must be supplied when posted=1',
+                {'field': 'posted_account_guid'})
+    else:
+        guid = gnucash.gnucash_core.GUID() 
+        gnucash.gnucash_core.GUIDString(posted_account_guid, guid)
+
+        posted_account = guid.AccountLookup(book)
+
+        if posted_account is None:
+            raise Error('NoAccount',
+                'No account exists with the posted account GUID',
+                {'field': 'posted_account_guid'})
+
+    bill.SetOwner(vendor)
+    bill.SetDateOpened(date_opened)
+    bill.SetNotes(notes)
+
+    # post if currently unposted and posted=1
+    if bill.GetDatePosted().strftime('%Y-%m-%d') == '1970-01-01' and posted == 1:
+        bill.PostToAccount(posted_account, posted_date, due_date, posted_memo,
+            posted_accumulatesplits, posted_autopay)
+
+    return gnucash_simple.billToDict(bill)
+
+def addEntry(book, invoice_id, date, description, account_guid, quantity, price):
+
+    invoice = getGnuCashInvoice(book, invoice_id)
+
+    if invoice is None:
+        raise Error('NoInvoice',
+            'No invoice exists with this ID', {'field': 'invoice_id'})
+
+    try:
+        date = datetime.datetime.strptime(date, "%Y-%m-%d")
+    except ValueError:
+        raise Error('InvalidDateOpened',
+            'The date opened must be provided in the form YYYY-MM-DD',
+            {'field': 'date'})
+
+    guid = gnucash.gnucash_core.GUID() 
+    gnucash.gnucash_core.GUIDString(account_guid, guid)
+
+    account = guid.AccountLookup(book)
+
+    if account is None:
+        raise Error('NoAccount', 'No account exists with this GUID',
+            {'field': 'account_guid'})
+
+    try:
+        quantity = Decimal(quantity).quantize(Decimal('.01'))
+    except ArithmeticError:
+        raise Error('InvalidQuantity', 'This quantity is not valid',
+            {'field': 'quantity'})
+
+    try:
+        price = Decimal(price).quantize(Decimal('.01'))
+    except ArithmeticError:
+        raise Error('InvalidPrice', 'This price is not valid',
+            {'field': 'price'})
+
+    entry = Entry(book, invoice, date.date())
+    entry.SetDateEntered(datetime.datetime.now())
+    entry.SetDescription(description)
+    entry.SetInvAccount(account)
+    entry.SetQuantity(gnc_numeric_from_decimal(quantity))
+    entry.SetInvPrice(gnc_numeric_from_decimal(price))
+
+    return gnucash_simple.entryToDict(entry)
+
+def addBillEntry(book, bill_id, date, description, account_guid, quantity,
+    price):
+
+    bill = getGnuCashBill(book,bill_id)
+
+    if bill is None:
+        raise Error('NoBill', 'No bill exists with this ID',
+            {'field': 'bill_id'})
+
+    try:
+        date = datetime.datetime.strptime(date, "%Y-%m-%d")
+    except ValueError:
+        raise Error('InvalidDateOpened',
+            'The date opened must be provided in the form YYYY-MM-DD',
+            {'field': 'date'})
+
+    guid = gnucash.gnucash_core.GUID() 
+    gnucash.gnucash_core.GUIDString(account_guid, guid)
+
+    account = guid.AccountLookup(book)
+
+    if account is None:
+        raise Error('NoAccount', 'No account exists with this GUID',
+            {'field': 'account_guid'})
+
+    try:
+        quantity = Decimal(quantity).quantize(Decimal('.01'))
+    except ArithmeticError:
+        raise Error('InvalidQuantity', 'This quantity is not valid',
+            {'field': 'quantity'})
+
+    try:
+        price = Decimal(price).quantize(Decimal('.01'))
+    except ArithmeticError:
+        raise Error('InvalidPrice', 'This price is not valid',
+            {'field': 'price'})
+    
+    entry = Entry(book, bill, date.date())
+    entry.SetDateEntered(datetime.datetime.now())
+    entry.SetDescription(description)
+    entry.SetBillAccount(account)
+    entry.SetQuantity(gnc_numeric_from_decimal(quantity))
+    entry.SetBillPrice(gnc_numeric_from_decimal(price))
+
+    return gnucash_simple.entryToDict(entry)
+
+def getEntry(book, entry_guid):
+
+    guid = gnucash.gnucash_core.GUID() 
+    gnucash.gnucash_core.GUIDString(entry_guid, guid)
+
+    entry = book.EntryLookup(guid)
+
+    if entry is None:
+        return None
+    else:
+        return gnucash_simple.entryToDict(entry)
+
+def updateEntry(book, entry_guid, date, description, account_guid, quantity,
+    price):
+
+    guid = gnucash.gnucash_core.GUID() 
+    gnucash.gnucash_core.GUIDString(entry_guid, guid)
+
+    entry = book.EntryLookup(guid)
+
+    if entry is None:
+        raise Error('NoEntry', 'No entry exists with this GUID',
+            {'field': 'entry_guid'})
+
+    try:
+        date = datetime.datetime.strptime(date, "%Y-%m-%d")
+    except ValueError:
+        raise Error('InvalidDateOpened',
+            'The date opened must be provided in the form YYYY-MM-DD',
+            {'field': 'date'})
+ 
+    gnucash.gnucash_core.GUIDString(account_guid, guid)
+
+    account = guid.AccountLookup(book)
+
+    if account is None:
+        raise Error('NoAccount', 'No account exists with this GUID',
+            {'field': 'account_guid'})
+
+    entry.SetDate(date.date())
+    entry.SetDateEntered(datetime.datetime.now())
+    entry.SetDescription(description)
+    entry.SetInvAccount(account)
+    entry.SetQuantity(
+        gnc_numeric_from_decimal(Decimal(quantity).quantize(Decimal('.01'))))
+    entry.SetInvPrice(
+        gnc_numeric_from_decimal(Decimal(price).quantize(Decimal('.01'))))
+
+    return gnucash_simple.entryToDict(entry)
+
+def deleteEntry(book, entry_guid):
+
+    guid = gnucash.gnucash_core.GUID() 
+    gnucash.gnucash_core.GUIDString(entry_guid, guid)
+
+    entry = book.EntryLookup(guid)
+
+    invoice = entry.GetInvoice()
+    bill = entry.GetBill()
+
+    if invoice != None and entry != None:
+        invoice.RemoveEntry(entry)
+    elif bill != None and entry != None:
+        bill.RemoveEntry(entry)
+
+    if entry != None:
+        entry.Destroy()
+
+def deleteTransaction(book, transaction_guid):
+
+    guid = gnucash.gnucash_core.GUID() 
+    gnucash.gnucash_core.GUIDString(transaction_guid, guid)
+
+    transaction = guid.TransLookup(book)
+
+    if transaction != None :
+        transaction.Destroy()
+
+def addBill(book, id, vendor_id, currency_mnumonic, date_opened, notes):
+
+    vendor = book.VendorLookupByID(vendor_id)
+
+    if vendor is None:
+        raise Error('NoVendor', 'A vendor with this ID does not exist',
+            {'field': 'id'})
+
+    if id is None:
+        id = book.BillNextID(vendor)
+
+    try:
+        date_opened = datetime.datetime.strptime(date_opened, "%Y-%m-%d")
+    except ValueError:
+        raise Error('InvalidVendorDateOpened',
+            'The date opened must be provided in the form YYYY-MM-DD',
+            {'field': 'date_opened'})
+
+    if currency_mnumonic is None:
+        currency_mnumonic = vendor.GetCurrency().get_mnemonic()
+
+    commod_table = book.get_table()
+    currency = commod_table.lookup('CURRENCY', currency_mnumonic)
+
+    if currency is None:
+        raise Error('InvalidVendorCurrency',
+            'A valid currency must be supplied for this vendor',
+            {'field': 'currency'})
+
+    bill = Bill(book, id, currency, vendor, date_opened.date())
+
+    bill.SetNotes(notes)
+
+    return gnucash_simple.billToDict(bill)
+
+def addAccount(book, name, currency_mnumonic, account_guid):
+
+    from gnucash.gnucash_core_c import \
+    ACCT_TYPE_ASSET, ACCT_TYPE_RECEIVABLE, ACCT_TYPE_INCOME, \
+    GNC_OWNER_CUSTOMER, ACCT_TYPE_LIABILITY
+
+    root_account = book.get_root_account()
+
+    commod_table = book.get_table()
+    currency = commod_table.lookup('CURRENCY', currency_mnumonic)
+
+    if currency is None:
+        raise Error('InvalidCustomerCurrency',
+            'A valid currency must be supplied for this customer',
+            {'field': 'currency'})
+
+    account = Account(book)
+    root_account.append_child(root_account)
+    account.SetName(name)
+    account.SetType(ACCT_TYPE_ASSET)
+    account.SetCommodity(currency)
+
+def addTransaction(book, num, description, date_posted, currency_mnumonic, splits):
+
+    transaction = Transaction(book)
+
+    transaction.BeginEdit()
+
+    commod_table = book.get_table()
+    currency = commod_table.lookup('CURRENCY', currency_mnumonic)
+
+    if currency is None:
+        raise Error('InvalidTransactionCurrency',
+            'A valid currency must be supplied for this transaction',
+            {'field': 'currency'})
+
+    try:
+        date_posted = datetime.datetime.strptime(date_posted, "%Y-%m-%d")
+    except ValueError:
+        raise Error('InvalidDatePosted',
+            'The date posted must be provided in the form YYYY-MM-DD',
+            {'field': 'date_posted'})
+
+
+    for split_values in splits:
+        account_guid = gnucash.gnucash_core.GUID() 
+        gnucash.gnucash_core.GUIDString(split_values['account_guid'], account_guid)
+
+        account = account_guid.AccountLookup(book)
+
+        if account is None:
+            raise Error('InvalidSplitAccount',
+                'A valid account must be supplied for this split',
+                {'field': 'account'})
+
+        split = Split(book)
+        split.SetValue(GncNumeric(split_values['value'], 100))
+        split.SetAccount(account)
+        split.SetParent(transaction)
+
+    transaction.SetCurrency(currency)
+    transaction.SetDescription(description)
+    transaction.SetNum(num)
+
+    transaction.SetDatePostedTS(date_posted)
+
+    transaction.CommitEdit()
+
+    return gnucash_simple.transactionToDict(transaction, ['splits'])
+
+def getTransaction(book, transaction_guid):
+
+    guid = gnucash.gnucash_core.GUID() 
+    gnucash.gnucash_core.GUIDString(transaction_guid, guid)
+
+    transaction = guid.TransLookup(book)
+
+    if transaction is None:
+        return None
+    else:
+        return gnucash_simple.transactionToDict(transaction, ['splits'])
+
+def editTransaction(book, transaction_guid, num, description, date_posted,
+    currency_mnumonic, splits):
+
+    guid = gnucash.gnucash_core.GUID() 
+    gnucash.gnucash_core.GUIDString(transaction_guid, guid)
+
+    transaction = guid.TransLookup(book)
+
+    if transaction is None:
+        raise Error('NoCustomer',
+            'A transaction with this GUID does not exist',
+            {'field': 'guid'})
+
+    transaction.BeginEdit()
+
+    commod_table = book.get_table()
+    currency = commod_table.lookup('CURRENCY', currency_mnumonic)
+
+    if currency is None:
+        raise Error('InvalidTransactionCurrency',
+            'A valid currency must be supplied for this transaction',
+            {'field': 'currency'})
+
+
+    try:
+        date_posted = datetime.datetime.strptime(date_posted, "%Y-%m-%d")
+    except ValueError:
+        raise Error('InvalidDatePosted',
+            'The date posted must be provided in the form YYYY-MM-DD',
+            {'field': 'date_posted'})
+
+    for split_values in splits:
+
+        split_guid = gnucash.gnucash_core.GUID() 
+        gnucash.gnucash_core.GUIDString(split_values['guid'], split_guid)
+
+        split = split_guid.SplitLookup(book)
+
+        if split is None:
+            raise Error('InvalidSplitGuid',
+                'A valid guid must be supplied for this split',
+                {'field': 'guid'})
+
+        account_guid = gnucash.gnucash_core.GUID() 
+        gnucash.gnucash_core.GUIDString(
+            split_values['account_guid'], account_guid)
+
+        account = account_guid.AccountLookup(book)
+
+        if account is None:
+            raise Error('InvalidSplitAccount',
+                'A valid account must be supplied for this split',
+                {'field': 'account'})
+
+        split.SetValue(GncNumeric(split_values['value'], 100))
+        split.SetAccount(account)
+        split.SetParent(transaction)
+
+    transaction.SetCurrency(currency)
+    transaction.SetDescription(description)
+    transaction.SetNum(num)
+
+    transaction.SetDatePostedTS(date_posted)
+
+    transaction.CommitEdit()
+
+    return gnucash_simple.transactionToDict(transaction, ['splits'])
+
+def gnc_numeric_from_decimal(decimal_value):
+    sign, digits, exponent = decimal_value.as_tuple()
+
+    # convert decimal digits to a fractional numerator
+    # equivlent to
+    # numerator = int(''.join(digits))
+    # but without the wated conversion to string and back,
+    # this is probably the same algorithm int() uses
+    numerator = 0
+    TEN = int(Decimal(0).radix()) # this is always 10
+    numerator_place_value = 1
+    # add each digit to the final value multiplied by the place value
+    # from least significant to most sigificant
+    for i in xrange(len(digits)-1,-1,-1):
+        numerator += digits[i] * numerator_place_value
+        numerator_place_value *= TEN
+
+    if decimal_value.is_signed():
+        numerator = -numerator
+
+    # if the exponent is negative, we use it to set the denominator
+    if exponent < 0 :
+        denominator = TEN ** (-exponent)
+    # if the exponent isn't negative, we bump up the numerator
+    # and set the denominator to 1
+    else:
+        numerator *= TEN ** exponent
+        denominator = 1
+
+    return GncNumeric(numerator, denominator)
 
 def shutdown():
-	session.end()
-	session.destroy()
+    session.save()
+    session.end()
+    session.destroy()
+    print 'Shutdown'
+
+class Error(Exception):
+    """Base class for exceptions in this module."""
+    def __init__(self, type, message, data):
+        self.type = type
+        self.message = message
+        self.data = data
 
 try:
-	options, arguments = getopt.getopt(sys.argv[1:], 'h:', ['host='])
+    options, arguments = getopt.getopt(sys.argv[1:], 'nh:', ['host=', 'new='])
 except getopt.GetoptError as err:
-	print str(err) # will print something like "option -a not recognized"
-	print 'Usage: python-rest.py <connection string>'
-	sys.exit(2)
+    print str(err) # will print something like "option -a not recognized"
+    print 'Usage: python-rest.py <connection string>'
+    sys.exit(2)
 
-if len(arguments) != 1:
-	print 'Usage: python-rest.py <connection string>'
-	sys.exit(2)
+if len(arguments) == 0:
+    print 'Usage: python-rest.py <connection string>'
+    sys.exit(2)
 
-#set default host for flash
+#set default host for Flask
 host = '127.0.0.1'
 
 #allow host option to be changed
 for option, value in options:
-	if option in ("-h", "--host"):
-		host = value
+    if option in ("-h", "--host"):
+        host = value
+
+is_new = False
+
+# allow a new database to be used
+for option, value in options:
+    if option in ("-n", "--new"):
+        is_new = True
+
 
 #start gnucash session base on connection string argument
+if is_new:
+    session = gnucash.Session(arguments[0], is_new=True)
+
+    # seem to get errors if we use the session directly, so save it and
+    #destroy it so it's no longer new
+
+    session.save()
+    session.end()
+    session.destroy()
+
 session = gnucash.Session(arguments[0], ignore_lock=True)
 
 # register method to close gnucash connection gracefully
 atexit.register(shutdown)
 
+app.debug = False
+
+# log to console
+if not app.debug:
+    import logging
+    from logging import StreamHandler
+    stream_handler = StreamHandler()
+    stream_handler.setLevel(logging.ERROR)
+    app.logger.addHandler(stream_handler)
+
 # start Flask server
 app.run(host=host)
diff --git a/src/optional/python-bindings/example_scripts/rest-api/gnucash_simple.py b/src/optional/python-bindings/example_scripts/rest-api/gnucash_simple.py
index 51e7632..509d7ac 100644
--- a/src/optional/python-bindings/example_scripts/rest-api/gnucash_simple.py
+++ b/src/optional/python-bindings/example_scripts/rest-api/gnucash_simple.py
@@ -1,152 +1,303 @@
+'''
+
+gnucash_simple.py -- A helper file to convert Gnucash objects into
+dictionaries for easier conversion to JSON
+
+Copyright (C) 2013 Tom Lofts <dev at loftx.co.uk>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of
+the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, contact:
+
+Free Software Foundation Voice: +1-617-542-5942
+51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
+Boston, MA 02110-1301, USA gnu at gnu.org
+
+ at author Tom Lofts <dev at loftx.co.uk>
+
+'''
+
 import gnucash
-from gnucash.gnucash_business import Entry
+from gnucash.gnucash_business import Entry, Split, Account
 
 def addressToDict(address):
-	if address is None:
-		return None
-	else:
-		simple_address = {}
-		simple_address['name'] = address.GetName();
-		simple_address['line_1'] = address.GetAddr1();
-		simple_address['line_2'] = address.GetAddr2();
-		simple_address['line_3'] = address.GetAddr3();
-		simple_address['line_4'] = address.GetAddr4();
-		simple_address['phone'] = address.GetPhone();
-		simple_address['fax'] = address.GetFax();
-		simple_address['email'] = address.GetEmail();
-
-		return simple_address
+    if address is None:
+        return None
+    else:
+        simple_address = {}
+        simple_address['name'] = address.GetName();
+        simple_address['line_1'] = address.GetAddr1();
+        simple_address['line_2'] = address.GetAddr2();
+        simple_address['line_3'] = address.GetAddr3();
+        simple_address['line_4'] = address.GetAddr4();
+        simple_address['phone'] = address.GetPhone();
+        simple_address['fax'] = address.GetFax();
+        simple_address['email'] = address.GetEmail();
+
+        return simple_address
 
 def vendorToDict(vendor):
 
-	if vendor is None:
-		return None
-	else:
-		simple_vendor = {}
-		simple_vendor['name'] = vendor.GetName()
-		simple_vendor['id'] =  vendor.GetID()
-		simple_vendor['notes'] =  vendor.GetNotes()
-		simple_vendor['active'] =  vendor.GetActive()
-		simple_vendor['currency'] =  vendor.GetCurrency().get_mnemonic()
-		simple_vendor['tax_table_override'] =  vendor.GetTaxTableOverride()
-		simple_vendor['address'] = addressToDict(vendor.GetAddr())
-		simple_vendor['tax_included'] =  vendor.GetTaxIncluded()
-
-		return simple_vendor
+    if vendor is None:
+        return None
+    else:
+        simple_vendor = {}
+        simple_vendor['name'] = vendor.GetName()
+        simple_vendor['id'] = vendor.GetID()
+        simple_vendor['guid'] = vendor.GetGUID().to_string()
+        simple_vendor['notes'] = vendor.GetNotes()
+        simple_vendor['active'] = vendor.GetActive()
+        simple_vendor['currency'] = vendor.GetCurrency().get_mnemonic()
+        simple_vendor['tax_table_override'] = vendor.GetTaxTableOverride()
+        simple_vendor['address'] = addressToDict(vendor.GetAddr())
+        simple_vendor['tax_included'] = vendor.GetTaxIncluded()
+
+        return simple_vendor
 
 def customerToDict(customer):
 
-	if customer is None:
-		return None
-	else:
-		simple_customer = {}
-		simple_customer['name'] = customer.GetName()
-		simple_customer['id'] =  customer.GetID()
-		simple_customer['notes'] =  customer.GetNotes()
-		simple_customer['active'] =  customer.GetActive()
-		simple_customer['discount'] =  customer.GetDiscount().to_double()
-		simple_customer['credit'] =  customer.GetCredit().to_double()
-		simple_customer['currency'] =  customer.GetCurrency().get_mnemonic()
-		simple_customer['tax_table_override'] =  customer.GetTaxTableOverride()
-		simple_customer['address'] = addressToDict(customer.GetAddr())
-		simple_customer['shipping_address'] = addressToDict(customer.GetShipAddr())
-		simple_customer['tax_included'] =  customer.GetTaxIncluded()
-
-		return simple_customer
-
-def transactionToDict(transaction):
-	if transaction is None:
-		return None
-	else:
-		simple_transaction = {}
-		simple_transaction['num'] =  transaction.GetNum()
-		simple_transaction['notes'] =  transaction.GetNotes()
-		simple_transaction['is_closing_txn'] =  transaction.GetIsClosingTxn()
-		simple_transaction['count_splits'] =  transaction.CountSplits()
-		simple_transaction['has_reconciled_splits'] =  transaction.HasReconciledSplits()
-		simple_transaction['currency'] =  transaction.GetCurrency().get_mnemonic()
-		simple_transaction['imbalance_value'] =  transaction.GetImbalanceValue().to_double()
-		simple_transaction['is_balanced'] =  transaction.IsBalanced()
-		simple_transaction['date'] =  transaction.GetDate()
-		simple_transaction['date_posted'] =  transaction.RetDatePostedTS().strftime('%Y-%m-%d')
-		simple_transaction['date_entered'] =  transaction.RetDateEnteredTS().strftime('%Y-%m-%d')
-		simple_transaction['date_due'] =  transaction.RetDateDueTS().strftime('%Y-%m-%d')
-		simple_transaction['void_status'] =  transaction.GetVoidStatus()
-		simple_transaction['void_time'] =  transaction.GetVoidTime().strftime('%Y-%m-%d')
-
-		return simple_transaction
+    if customer is None:
+        return None
+    else:
+        simple_customer = {}
+        simple_customer['name'] = customer.GetName()
+        simple_customer['id'] = customer.GetID()
+        simple_customer['guid'] = customer.GetGUID().to_string()
+        simple_customer['notes'] = customer.GetNotes()
+        simple_customer['active'] = customer.GetActive()
+        simple_customer['discount'] = customer.GetDiscount().to_double()
+        simple_customer['credit'] = customer.GetCredit().to_double()
+        simple_customer['currency'] = customer.GetCurrency().get_mnemonic()
+        simple_customer['tax_table_override'] = customer.GetTaxTableOverride()
+        simple_customer['address'] = addressToDict(customer.GetAddr())
+        simple_customer['shipping_address'] = addressToDict(
+            customer.GetShipAddr())
+        simple_customer['tax_included'] = customer.GetTaxIncluded()
+
+        return simple_customer
+
+def transactionToDict(transaction, entities):
+    if transaction is None:
+        return None
+    else:
+        simple_transaction = {}
+        simple_transaction['guid'] = transaction.GetGUID().to_string()
+        simple_transaction['num'] = transaction.GetNum()
+        simple_transaction['notes'] = transaction.GetNotes()
+        simple_transaction['is_closing_txn'] = transaction.GetIsClosingTxn()
+        
+        if 'splits' in entities:
+            simple_transaction['splits'] = []
+            for split in transaction.GetSplitList(): 
+                if type(split) != Split:
+                    split=Split(instance=split) 
+                simple_transaction['splits'].append(
+                    splitToDict(split, ['account']))
+
+        simple_transaction['count_splits'] = transaction.CountSplits()
+        simple_transaction['has_reconciled_splits'] = \
+            transaction.HasReconciledSplits()
+        simple_transaction['currency'] = transaction.GetCurrency(
+            ).get_mnemonic()
+        simple_transaction['imbalance_value'] = transaction.GetImbalanceValue(
+            ).to_double()
+        simple_transaction['is_balanced'] = transaction.IsBalanced()
+        simple_transaction['date'] = transaction.GetDate()
+        simple_transaction['date_posted'] = transaction.RetDatePostedTS(
+            ).strftime('%Y-%m-%d')
+        simple_transaction['date_entered'] = transaction.RetDateEnteredTS(
+            ).strftime('%Y-%m-%d')
+        simple_transaction['date_due'] = transaction.RetDateDueTS().strftime(
+            '%Y-%m-%d')
+        simple_transaction['void_status'] = transaction.GetVoidStatus()
+        simple_transaction['void_time'] = transaction.GetVoidTime().strftime(
+            '%Y-%m-%d')
+
+        simple_transaction['description'] = transaction.GetDescription()
 
+        return simple_transaction
+
+def splitToDict(split, entities):
+    if split is None:
+        return None
+    else:
+        simple_split = {}
+        simple_split['guid'] = split.GetGUID().to_string()
+        if 'account' in entities:
+            simple_split['account'] = accountToDict(split.GetAccount())
+        if 'transaction' in entities:
+            simple_split['transaction'] = transactionToDict(
+                split.GetParent(), [])      
+        if 'other_split' in entities:
+            simple_split['other_split'] = splitToDict(
+                split.GetOtherSplit(), ['account'])
+        simple_split['amount'] = split.GetAmount().to_double()
+        simple_split['value'] = split.GetValue().to_double()
+        simple_split['balance'] = split.GetBalance().to_double()
+        simple_split['cleared_balance'] = split.GetClearedBalance().to_double()
+        simple_split['reconciled_balance'] = split.GetReconciledBalance(
+            ).to_double()
+
+        return simple_split
 def invoiceToDict(invoice):
 
-	if invoice is None:
-		return None
-	else:
-		simple_invoice = {}
-		simple_invoice['id'] =  invoice.GetID()
-		simple_invoice['type'] =  invoice.GetType()
-		simple_invoice['date_opened'] =  invoice.GetDateOpened().strftime('%Y-%m-%d')
-		simple_invoice['date_posted'] =  invoice.GetDatePosted().strftime('%Y-%m-%d')
-		simple_invoice['date_due'] =  invoice.GetDateDue().strftime('%Y-%m-%d')
-		simple_invoice['notes'] =  invoice.GetNotes()
-		simple_invoice['active'] =  invoice.GetActive()
-		simple_invoice['currency'] =  invoice.GetCurrency().get_mnemonic()
-		simple_invoice['owner'] =  vendorToDict(invoice.GetOwner())
-		simple_invoice['owner_type'] =  invoice.GetOwnerType()
-		simple_invoice['billing_id'] =  invoice.GetBillingID()
-		simple_invoice['to_charge_amount'] = invoice.GetToChargeAmount().to_double()
-		simple_invoice['total'] = invoice.GetTotal().to_double()
-		simple_invoice['total_subtotal'] = invoice.GetTotalSubtotal().to_double()
-		simple_invoice['total_tax'] = invoice.GetTotalTax().to_double()
-		simple_invoice['entries'] = {}
-		for n, entry in enumerate(invoice.GetEntries()):
-			if type(entry) != Entry:
-				entry=Entry(instance=entry)
-			simple_invoice['entries'][n] = entryToDict(entry)
-		simple_invoice['posted'] = invoice.IsPosted()
-		simple_invoice['paid'] = invoice.IsPaid()
-
-		return simple_invoice
+    if invoice is None:
+        return None
+    else:
+        simple_invoice = {}
+        simple_invoice['id'] = invoice.GetID()
+        simple_invoice['type'] = invoice.GetType()
+        simple_invoice['date_opened'] = invoice.GetDateOpened().strftime(
+            '%Y-%m-%d')
+        if invoice.GetDatePosted().strftime('%Y-%m-%d') == '1970-01-01':
+            simple_invoice['date_posted'] = None
+        else:
+            simple_invoice['date_posted'] = invoice.GetDatePosted().strftime(
+                '%Y-%m-%d')
+        if invoice.GetDateDue().strftime('%Y-%m-%d') == '1970-01-01':
+            simple_invoice['date_due'] = None
+        else:
+            simple_invoice['date_due'] = invoice.GetDateDue().strftime(
+                '%Y-%m-%d')
+        simple_invoice['notes'] = invoice.GetNotes()
+        simple_invoice['active'] = invoice.GetActive()
+        simple_invoice['currency'] = invoice.GetCurrency().get_mnemonic()
+        simple_invoice['owner'] = vendorToDict(invoice.GetOwner()) 
+        simple_invoice['owner_type'] = invoice.GetOwnerType()
+        simple_invoice['billing_id'] = invoice.GetBillingID()
+        simple_invoice['to_charge_amount'] = invoice.GetToChargeAmount().to_double()
+        simple_invoice['posted_txn'] = transactionToDict(invoice.GetPostedTxn(), [])
+        simple_invoice['total'] = invoice.GetTotal().to_double()
+        simple_invoice['total_subtotal'] = invoice.GetTotalSubtotal(
+            ).to_double()
+        simple_invoice['total_tax'] = invoice.GetTotalTax().to_double()
+
+        simple_invoice['entries'] = []
+        for n, entry in enumerate(invoice.GetEntries()):
+            if type(entry) != Entry:
+                entry=Entry(instance=entry) 
+            simple_invoice['entries'].append(entryToDict(entry))
+
+        simple_invoice['posted'] = invoice.IsPosted()
+        simple_invoice['paid'] = invoice.IsPaid()
+
+        return simple_invoice
+
+def billToDict(bill):
+
+    if bill is None:
+        return None
+    else:
+        simple_bill = {}
+        simple_bill['id'] = bill.GetID()
+        simple_bill['type'] = bill.GetType()
+        simple_bill['date_opened'] = bill.GetDateOpened().strftime('%Y-%m-%d')
+        if bill.GetDatePosted().strftime('%Y-%m-%d') == '1970-01-01':
+            simple_bill['date_posted'] = None
+        else:
+            simple_bill['date_posted'] = bill.GetDatePosted().strftime(
+                '%Y-%m-%d')
+        if bill.GetDateDue().strftime('%Y-%m-%d') == '1970-01-01':
+            simple_bill['date_due'] = None
+        else:
+            simple_bill['date_due'] = bill.GetDateDue().strftime('%Y-%m-%d')
+        simple_bill['notes'] = bill.GetNotes()
+        simple_bill['active'] = bill.GetActive()
+        simple_bill['currency'] = bill.GetCurrency().get_mnemonic()
+        simple_bill['owner'] = vendorToDict(bill.GetOwner()) 
+        simple_bill['owner_type'] = bill.GetOwnerType()
+        simple_bill['billing_id'] = bill.GetBillingID()
+        simple_bill['to_charge_amount'] = bill.GetToChargeAmount().to_double()
+        simple_bill['total'] = bill.GetTotal().to_double()
+        simple_bill['total_subtotal'] = bill.GetTotalSubtotal().to_double()
+        simple_bill['total_tax'] = bill.GetTotalTax().to_double()
+
+        simple_bill['entries'] = []
+        for n, entry in enumerate(bill.GetEntries()):
+            if type(entry) != Entry:
+                entry=Entry(instance=entry) 
+            simple_bill['entries'].append(entryToDict(entry))
+
+        simple_bill['posted'] = bill.IsPosted()
+        simple_bill['paid'] = bill.IsPaid()
+
+        return simple_bill
 
 def entryToDict(entry):
 
-	if entry is None:
-		return None
-	else:
-		simple_entry = {}
-		simple_entry['date'] =  entry.GetDate().strftime('%Y-%m-%d')
-		simple_entry['date_entered'] =  entry.GetDateEntered().strftime('%Y-%m-%d')
-		simple_entry['description'] =  entry.GetDescription()
-		simple_entry['action'] =  entry.GetAction()
-		simple_entry['notes'] =  entry.GetNotes()
-		simple_entry['quantity'] = gnucash.GncNumeric(instance=entry.GetQuantity()).to_double()
-		simple_entry['inv_price'] = gnucash.GncNumeric(instance=entry.GetInvPrice()).to_double()
-		simple_entry['discount'] = gnucash.GncNumeric(instance=entry.GetInvDiscount()).to_double()
-		simple_entry['discounted_type'] =  entry.GetInvDiscountType()
-		simple_entry['discounted_how'] =  entry.GetInvDiscountHow()
-		simple_entry['inv_taxable'] =  entry.GetInvTaxable()
-		simple_entry['inv_tax_included'] =  entry.GetInvTaxIncluded()
-		simple_entry['inv_tax_table_override'] =  entry.GetInvTaxTable()
-		simple_entry['bill_price'] = gnucash.GncNumeric(instance=entry.GetBillPrice()).to_double()
-		simple_entry['bill_taxable'] =  entry.GetBillTaxable()
-		simple_entry['bill_tax_included'] =  entry.GetBillTaxIncluded()
-		simple_entry['bill_tax_table'] =  entry.GetBillTaxTable()
-		simple_entry['billable'] =  entry.GetBillable()
-		simple_entry['bill_payment'] =  entry.GetBillPayment()
-		simple_entry['is_open'] =  entry.IsOpen()
-
-		return simple_entry
+    if entry is None:
+        return None
+    else:
+
+        simple_entry = {}
+        simple_entry['guid'] = entry.GetGUID().to_string()
+        simple_entry['date'] = entry.GetDate().strftime('%Y-%m-%d')
+        simple_entry['date_entered'] = entry.GetDateEntered().strftime(
+            '%Y-%m-%d')
+        simple_entry['description'] = entry.GetDescription()
+        simple_entry['action'] = entry.GetAction()
+        simple_entry['notes'] = entry.GetNotes()
+        simple_entry['quantity'] = entry.GetQuantity().to_double()
+        if entry.GetInvAccount() == None:
+            simple_entry['inv_account'] = {}
+        else: 
+            simple_entry['inv_account'] = accountToDict(entry.GetInvAccount())      
+        simple_entry['inv_price'] = entry.GetInvPrice().to_double()
+        simple_entry['discount'] = entry.GetInvDiscount().to_double()
+        simple_entry['discounted_type'] = entry.GetInvDiscountType()
+        simple_entry['discounted_how'] = entry.GetInvDiscountHow()
+        simple_entry['inv_taxable'] = entry.GetInvTaxable()
+        simple_entry['inv_tax_included'] = entry.GetInvTaxIncluded()
+        simple_entry['inv_tax_table_override'] = entry.GetInvTaxTable()
+        if entry.GetBillAccount() == None:
+            simple_entry['bill_account'] = {}
+        else: 
+            simple_entry['bill_account'] = accountToDict(
+                entry.GetBillAccount()) 
+        simple_entry['bill_price'] = entry.GetBillPrice().to_double()
+        simple_entry['bill_taxable'] = entry.GetBillTaxable()
+        simple_entry['bill_tax_included'] = entry.GetBillTaxIncluded()
+        simple_entry['bill_tax_table'] = entry.GetBillTaxTable()
+        simple_entry['billable'] = entry.GetBillable()
+        simple_entry['bill_payment'] = entry.GetBillPayment()
+        simple_entry['is_open'] = entry.IsOpen()
+
+        return simple_entry
 
 
 def accountToDict(account):
 
-	if account is None:
-		return None
-	else:
-		simple_account = {}
-		simple_account['name'] =  account.GetName()
-		simple_account['guid'] =  account.GetGUID().to_string()
-		simple_account['subaccounts'] =  []
-		for n, subaccount in enumerate(account.get_children_sorted()):
-			simple_account['subaccounts'].append(accountToDict(subaccount))
-
-		return simple_account
\ No newline at end of file
+    commod_table = account.get_book().get_table()
+    gbp = commod_table.lookup('CURRENCY', 'GBP')
+
+    if account is None:
+        return None
+    else:
+        simple_account = {}
+        simple_account['name'] = account.GetName()
+        simple_account['type_id'] = account.GetType()
+        simple_account['description'] = account.GetDescription()
+        simple_account['guid'] = account.GetGUID().to_string()
+        if account.GetCommodity() == None:
+            simple_account['currency'] = ''
+        else:
+            simple_account['currency'] = account.GetCommodity().get_mnemonic()
+        simple_account['subaccounts'] = []
+        for n, subaccount in enumerate(account.get_children_sorted()):
+            simple_account['subaccounts'].append(accountToDict(subaccount))
+
+        simple_account['balance'] = account.GetBalance().to_double()
+        simple_account['balance_gbp'] = account.GetBalanceInCurrency(
+            gbp, True).to_double()
+        simple_account['placeholder'] = account.GetPlaceholder()
+
+        return simple_account
diff --git a/src/optional/python-bindings/gnucash_business.py b/src/optional/python-bindings/gnucash_business.py
index 930c6d8..b03ce01 100644
--- a/src/optional/python-bindings/gnucash_business.py
+++ b/src/optional/python-bindings/gnucash_business.py
@@ -35,7 +35,7 @@ from function_class import \
 
 from gnucash_core import \
      GnuCashCoreClass, GncNumeric, GncCommodity, Transaction, \
-     Split, Book, GncLot, Account
+     Split, Book, GncLot, Account, GUID
 
 from gnucash_core_c import GNC_OWNER_CUSTOMER, GNC_OWNER_JOB, \
     GNC_OWNER_EMPLOYEE, GNC_OWNER_VENDOR, \
@@ -217,8 +217,7 @@ class Entry(GnuCashCoreClass):
             if invoice != None:
                 invoice.AddEntry(self)
         else:
-
-            GnuCashCoreClass.__init__(self, instance=instance)
+            GnuCashCoreClass.__init__(self, instance=instance)    
 
     def test_type(self, invoice):
         if invoice.GetTypeString() == "Invoice" and self.GetInvoice() == None:
@@ -226,11 +225,11 @@ class Entry(GnuCashCoreClass):
         if invoice.GetTypeString() == "Bill" and self.GetBill() == None:
             raise Exception("Entry type error. Check that Entry type matches Bill.")
 
-
 # Owner
 GnuCashBusinessEntity.add_methods_with_prefix('gncOwner')
 
 owner_dict = {
+                    'GetGUID' : GUID,
                     'GetCustomer' : Customer,
                     'GetVendor' : Vendor,
                     'GetEmployee' : Employee,
@@ -249,6 +248,7 @@ methods_return_instance_lists(
 
 # Customer
 Customer.add_constructor_and_methods_with_prefix('gncCustomer', 'Create')
+Customer.add_method('gncOwnerApplyPayment', 'ApplyPayment')
 
 customer_dict = {
                     'GetAddr' : Address,
@@ -326,6 +326,8 @@ Invoice.add_constructor_and_methods_with_prefix('gncInvoice', 'Create')
 methods_return_instance_lists(
     Invoice, { 'GetEntries': Entry })
 
+Invoice.add_method('gncInvoiceRemoveEntry', 'RemoveEntry')
+
 # Bill
 Bill.add_methods_with_prefix('gncBill')
 
@@ -351,7 +353,11 @@ Invoice.decorate_functions(
 # Entry
 Entry.add_constructor_and_methods_with_prefix('gncEntry', 'Create')
 
+Entry.add_method('gncEntryGetGUID', 'GetGUID')
+Entry.add_method('gncEntryDestroy', 'Destroy')
+
 entry_dict = {
+                 'GetGUID' : GUID,
                  'GetQuantity': GncNumeric,
                  'GetInvAccount': Account,
                  'GetInvPrice': GncNumeric,
diff --git a/src/optional/python-bindings/gnucash_core.i b/src/optional/python-bindings/gnucash_core.i
index 13aef43..213c823 100644
--- a/src/optional/python-bindings/gnucash_core.i
+++ b/src/optional/python-bindings/gnucash_core.i
@@ -124,23 +124,23 @@
     PyObject * swig_wrapper_object;
     if (owner_type == GNC_OWNER_CUSTOMER ){
         swig_wrapper_object = SWIG_NewPointerObj(
-	    gncOwnerGetCustomer($1), $descriptor(GncCustomer *), 0);
+        gncOwnerGetCustomer($1), $descriptor(GncCustomer *), 0);
     }
     else if (owner_type == GNC_OWNER_JOB){
         swig_wrapper_object = SWIG_NewPointerObj(
-	    gncOwnerGetJob($1), $descriptor(GncJob *), 0);
+        gncOwnerGetJob($1), $descriptor(GncJob *), 0);
     }
     else if (owner_type == GNC_OWNER_VENDOR){
         swig_wrapper_object = SWIG_NewPointerObj(
-	    gncOwnerGetVendor($1), $descriptor(GncVendor *), 0);
+        gncOwnerGetVendor($1), $descriptor(GncVendor *), 0);
     }
     else if (owner_type == GNC_OWNER_EMPLOYEE){
         swig_wrapper_object = SWIG_NewPointerObj(
-	    gncOwnerGetEmployee($1), $descriptor(GncEmployee *), 0);
+        gncOwnerGetEmployee($1), $descriptor(GncEmployee *), 0);
     }
     else {
         swig_wrapper_object = Py_None;
-	Py_INCREF(Py_None);
+    Py_INCREF(Py_None);
     }
     PyTuple_SetItem(owner_tuple, 1, swig_wrapper_object);
     $result = owner_tuple;
@@ -175,10 +175,10 @@
         $1 = temp_owner;
     }
     else {
-	PyErr_SetString(
-	    PyExc_ValueError,
-	    "Python object passed to function with GncOwner * argument "
-	    "couldn't be converted back to pointer of that type");
+    PyErr_SetString(
+        PyExc_ValueError,
+        "Python object passed to function with GncOwner * argument "
+        "couldn't be converted back to pointer of that type");
         return NULL;
     }
 }
@@ -187,6 +187,7 @@
     gncOwnerFree($1);
 }
 
+static const GncGUID * gncEntryGetGUID(GncEntry *x);
 
 %include <gnc-lot.h>
 
diff --git a/src/optional/python-bindings/gnucash_core.py b/src/optional/python-bindings/gnucash_core.py
index 578e800..af9e007 100644
--- a/src/optional/python-bindings/gnucash_core.py
+++ b/src/optional/python-bindings/gnucash_core.py
@@ -40,8 +40,9 @@ from gnucash_core_c import gncInvoiceLookup, gncInvoiceGetInvoiceFromTxn, \
     gncInvoiceGetInvoiceFromLot, gncEntryLookup, gncInvoiceLookup, \
     gncCustomerLookup, gncVendorLookup, gncJobLookup, gncEmployeeLookup, \
     gncTaxTableLookup, gncTaxTableLookupByName, gnc_search_invoice_on_id, \
-    gnc_search_customer_on_id, gnc_search_bill_on_id , gnc_search_vendor_on_id, \
-    gncInvoiceNextID, gncCustomerNextID, gncTaxTableGetTables, gncVendorNextID
+    gnc_search_customer_on_id, gnc_search_bill_on_id , \
+    gnc_search_vendor_on_id, gncInvoiceNextID, gncCustomerNextID, \
+    gncVendorNextID, gncTaxTableGetTables
 
 class GnuCashCoreClass(ClassFromFunctions):
     _module = gnucash_core_c
@@ -176,7 +177,7 @@ class Book(GnuCashCoreClass):
             gncInvoiceLookup, Invoice, guid.get_instance() )
 
     def EntryLookup(self, guid):
-        from gnucash_business import Entr
+        from gnucash_business import Entry
         return self.do_lookup_create_oo_instance(
             gncEntryLookup, Entry, guid.get_instance() )
 
@@ -214,7 +215,7 @@ class Book(GnuCashCoreClass):
         from gnucash_business import TaxTable
         return [ TaxTable(instance=item) for item in gncTaxTableGetTables(self.instance) ]
 
-    def BillLoookupByID(self, id):
+    def BillLookupByID(self, id):
         from gnucash_business import Bill
         return self.do_lookup_create_oo_instance(
             gnc_search_bill_on_id, Bill, id)
@@ -249,7 +250,7 @@ class Book(GnuCashCoreClass):
       ''' Return the next Customer ID. '''
       from gnucash.gnucash_core_c import gncCustomerNextID
       return gncCustomerNextID(self.get_instance())
-    
+
     def VendorNextID(self):
       ''' Return the next Vendor ID. '''
       from gnucash.gnucash_core_c import gncVendorNextID
@@ -581,6 +582,9 @@ methods_return_instance(GncLot, gnclot_dict)
 Transaction.add_methods_with_prefix('xaccTrans')
 Transaction.add_method('gncTransGetGUID', 'GetGUID');
 
+Transaction.add_method('xaccTransGetDescription', 'GetDescription')
+Transaction.add_method('xaccTransDestroy', 'Destroy')
+
 trans_dict =    {
                     'GetSplit': Split,
                     'FindSplitByAccount': Split,
@@ -606,6 +610,7 @@ Transaction.decorate_functions(
 # Split
 Split.add_methods_with_prefix('xaccSplit')
 Split.add_method('gncSplitGetGUID', 'GetGUID');
+Split.add_method('xaccSplitDestroy', 'Destroy')
 
 split_dict =    {
                     'GetBook': Book,
@@ -634,6 +639,7 @@ Split.parent = property( Split.GetParent, Split.SetParent )
 Account.add_methods_with_prefix('xaccAccount')
 Account.add_methods_with_prefix('gnc_account_')
 Account.add_method('gncAccountGetGUID', 'GetGUID');
+Account.add_method('xaccAccountGetPlaceholder', 'GetPlaceholder')
 
 account_dict =  {
                     'get_book' : Book,
@@ -734,19 +740,36 @@ Query.add_method('qof_query_search_for', 'search_for')
 Query.add_method('qof_query_run', 'run')
 Query.add_method('qof_query_add_term', 'add_term')
 Query.add_method('qof_query_add_boolean_match', 'add_boolean_match')
+Query.add_method('qof_query_add_guid_list_match', 'add_guid_list_match')
+Query.add_method('qof_query_add_guid_match', 'add_guid_match')
 Query.add_method('qof_query_destroy', 'destroy')
 
 class QueryStringPredicate(GnuCashCoreClass):
     pass
 
-QueryStringPredicate.add_constructor_and_methods_with_prefix('qof_query_', 'string_predicate')
+QueryStringPredicate.add_constructor_and_methods_with_prefix(
+    'qof_query_','string_predicate')
 
 class QueryBooleanPredicate(GnuCashCoreClass):
     pass
 
-QueryBooleanPredicate.add_constructor_and_methods_with_prefix('qof_query_', 'boolean_predicate')
+QueryBooleanPredicate.add_constructor_and_methods_with_prefix(
+    'qof_query_', 'boolean_predicate')
 
 class QueryInt32Predicate(GnuCashCoreClass):
     pass
 
-QueryInt32Predicate.add_constructor_and_methods_with_prefix('qof_query_', 'int32_predicate')
+QueryInt32Predicate.add_constructor_and_methods_with_prefix(
+    'qof_query_', 'int32_predicate')
+
+class QueryDatePredicate(GnuCashCoreClass):
+    pass
+
+QueryDatePredicate.add_constructor_and_methods_with_prefix(
+    'qof_query_', 'date_predicate')
+
+class QueryGuidPredicate(GnuCashCoreClass):
+    pass
+
+QueryGuidPredicate.add_constructor_and_methods_with_prefix(
+    'qof_query_', 'guid_predicate')



Summary of changes:
 .../example_scripts/rest-api/README                |  230 ++-
 .../example_scripts/rest-api/gnucash_rest.py       | 1912 ++++++++++++++++++--
 .../example_scripts/rest-api/gnucash_simple.py     |  417 +++--
 src/optional/python-bindings/gnucash_business.py   |   14 +-
 src/optional/python-bindings/gnucash_core.i        |   19 +-
 src/optional/python-bindings/gnucash_core.py       |   39 +-
 6 files changed, 2337 insertions(+), 294 deletions(-)
 mode change 100755 => 100644 src/optional/python-bindings/example_scripts/rest-api/gnucash_rest.py



More information about the gnucash-changes mailing list