Files
gnucash/bindings/python/example_scripts/rest-api/gnucash_rest.py
2020-06-21 16:23:32 +02:00

1916 lines
60 KiB
Python

#!/usr/bin/env python3
'''
gnucash_rest.py -- A Flask app which responds to REST requests
with JSON responses
Copyright (C) 2013 Tom Lofts <dev@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@gnu.org
@author Tom Lofts <dev@loftx.co.uk>
'''
import gnucash
import gnucash_simple
import json
import atexit
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, \
QOF_QUERY_NAND, \
QOF_QUERY_NOR, \
QOF_QUERY_XOR
from gnucash import \
QOF_STRING_MATCH_NORMAL, \
QOF_STRING_MATCH_CASEINSENSITIVE
from gnucash import \
QOF_COMPARE_LT, \
QOF_COMPARE_LTE, \
QOF_COMPARE_EQUAL, \
QOF_COMPARE_GT, \
QOF_COMPARE_GTE, \
QOF_COMPARE_NEQ
from gnucash import \
INVOICE_TYPE
from gnucash import \
INVOICE_IS_PAID
from gnucash import SessionOpenMode
app = Flask(__name__)
app.debug = True
@app.route('/accounts', methods=['GET', 'POST'])
def api_accounts():
if request.method == 'GET':
accounts = getAccounts(session.book)
return Response(json.dumps(accounts), mimetype='application/json')
elif request.method == 'POST':
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')
else:
abort(405)
@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')
@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')
@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)
@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)
@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)
@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)
@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)
@app.route('/invoices', methods=['GET', 'POST'])
def api_invoices():
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)
@app.route('/invoices/<id>', methods=['GET', 'POST', 'PAY'])
def api_invoice(id):
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)
@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)
@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)
@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)
@app.route('/customers/<id>', methods=['GET', 'POST'])
def api_customer(id):
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)
@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')
@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)
@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)
@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')
def getCustomers(book):
query = gnucash.Query()
query.search_for('gncCustomer')
query.set_book(book)
customers = []
for result in query.run():
customers.append(gnucash_simple.customerToDict(
gnucash.gnucash_business.Customer(instance=result)))
query.destroy()
return customers
def getCustomer(book, id):
customer = book.CustomerLookupByID(id)
if customer is None:
return None
else:
return gnucash_simple.customerToDict(customer)
def getVendors(book):
query = gnucash.Query()
query.search_for('gncVendor')
query.set_book(book)
vendors = []
for result in query.run():
vendors.append(gnucash_simple.vendorToDict(
gnucash.gnucash_business.Vendor(instance=result)))
query.destroy()
return vendors
def getVendor(book, id):
vendor = book.VendorLookupByID(id)
if vendor is None:
return None
else:
return gnucash_simple.vendorToDict(vendor)
def getAccounts(book):
accounts = gnucash_simple.accountToDict(book.get_root_account())
return accounts
def getAccountsFlat(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 = []
type_ids = [9]
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 list(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 = 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'
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 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)
splits = []
for split in query.run():
splits.append(gnucash_simple.splitToDict(
gnucash.gnucash_business.Split(instance=split),
['account', 'transaction', 'other_split']))
query.destroy()
return splits
def getInvoices(book, customer, is_paid, is_active, date_due_from,
date_due_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_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):
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 negative 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'})
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 range(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.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:], '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)
if len(arguments) == 0:
print('Usage: python-rest.py <connection string>')
sys.exit(2)
#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
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], SessionOpenMode.SESSION_NEW_STORE)
# 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()
# unsure about SESSION_BREAK_LOCK - it used to be ignore_lock=True
session = gnucash.Session(arguments[0], SessionOpenMode.SESSION_BREAK_LOCK)
# 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)