Welcome to the piecash documentation!

Release

1.2.0

Date

Oct 19, 2021

Authors

sdementen

Project page

https://github.com/sdementen/piecash

What’s new

Version 1.2.0 (2021-10-19)

  • fix changes from yahoo API (fix user-agent to “”, remove recovery of crumble)

  • fix tzlocal>=2.3 issue with change of tz type returned by tzlocal.get_localzone() (fix #180 #185)

  • move to github actions

  • remove contrib.napoleon as included in sphinx as of 1.3

  • change pytest.yield_fixture to pytest.fixture

  • remove unused code

Version 1.1.7 (2021-04-04)

  • fix issue when deleting splits (fix #155)

  • reformat files with black

Version 1.1.6 (2021-04-03)

  • add at_date parameter to Account.get_balance (tx @rvijayc, @gregwalters)

Version 1.1.5 (2021-03-21)

  • fix ledger export truncation of prices (fix #158)

  • fix ledger export to order transactions by date (fix #159)

Version 1.1.4 (2021-01-29)

  • allow tags with zero quantity by fixing unit price calculation (fix #153, tx @croth1)

  • allow tags with zero quantity of value by fixing validation control (fix #154, tx @stoklund)

  • use template0 when creating new books in postgres (vs template1 before) to fix encoding issues

  • add examples for deleting an account and exporting transactions to a CSV file

Version 1.1.3 (2021-01-17)

  • requires sqlalchemy < 1.4 (fix #149)

  • fix example with wrong post_date type

  • update currency_ISO

  • update use of deprecated function in xml (fix #147, tx @bxbrenden)

  • add example of program to modify an existing transaction

Version 1.1.2 (2020-10-24)

Version 1.1.1 (2020-10-21)

  • add a check_exists flag to allow bypassing check existence of DB on opening (fix #91, tx @williamjacksn)

Version 1.1.0 (2020-10-20)

  • fix use of ISO date for ledger export (fix #115 by @MisterY)

  • add field is_credit and is_debit to split (fix #105)

  • fix get_balance sign when recursing + add natural_sign keyword to specify if sign should be reverse or not

  • add support for Gnucash 4.1 (fix #136)

  • fix table names not matching in case (fix #137)

  • fix test suite to support 3.8

  • deprecate python 3.5

  • quandl will retrieve API KEY from environment variable QUANDL_API_KEY (if defined)

  • yahoo will use exchangeTimezoneName for timezone (vs exchangeTimezoneShortName before), thanks @geoffwright240

  • add possibility to export accounts with their short name in ledger (fix #123)

Version 1.0.0 (2019-04-15)

  • drop support of py27 and py34 (fix #53)

  • support gnucash 3.0.x format (code + test and book migration)

  • set autoflush to False for open_book (was only done for create_book before) (fix #93)

  • remove tz info when serialising DateTime to SQL (issue with postgresql doing some TZ conversion)

  • add basic support for Jobs

Version 0.18.0 (2018-04-24)

Mostly refactoring: - refactor common parts of vendor, customer and employee into person - add ‘on_book_add’ protocol called when object is added to a book - set autoflush to False to prevent weird behavior when using slots (that retrigger a query in the middle of a flush) - refactor slots - align sql schema 100% with 2.6.21 (based on sqlite reference) - support business slots

Version 0.17.0 (2018-03-16)

  • internal refactoring of setup.py

  • add optional packages

  • move to pipenv

  • improve documentation

  • fix missing extra blank between account name and amount in ledger export (fix #86)

Version 0.16.0 (2018-03-04)

  • add a documentation section about piecash on android

  • fix yahoo finance quote retrieval

  • indicate correct reconcile state in ledger output (fix #77)

Version 0.15.0 (2018-02-21)

  • add piecash CLI (refactor of scripts)

  • add book.invoices to retrieve all invoices in a book

  • expose gnucash rationals as decimals in Entry and Invoice

  • fix issue #65 about “template” (scheduled transactions) appearing in ledger export

  • fix issue #64 about escaping in double quote mnemonic with non alpha characters

  • fix issue #19 allowing to pass the check_same_thread flag for sqlite

  • add argument recurse to get_balance (fix #73)

  • handle currency conversion in get_balance

  • add Commodity.currency_conversion to get a conversion factor between a commodity and a currency

Version 0.14.1 (2018-02-01)

  • fix bug in pc-export

Version 0.14.0 (2018-02-01)

  • fix definition of account get_balance to use quantities (and not values) (@sdementen)

  • fix bug when providing a float instead of a Decimal to a numeric value (@gregorias)

  • support new format for date for 2.7/2.8 (@MisterY, @sdementen)

  • fix bug where transactions based on deleted scheduled transactions cause exceptions (@spookylukey)

  • fix bug (#58) where large Decimals where raising an sql exception instead of a ValueError exception (@sdementen)

  • add Recurrence to global imports + add documentation to Recurrence (@MisterY)

  • add script pc-export to export customers and vendors from a gnucash book (@sdementen)

Version 0.13.0 (2017-10-08)

  • upgrade CI (appveyor and travis) to 2.7/3.4/3.5/3.6

  • upgrade dependencies

  • df_splits: allow user to specify additional fields to extract (@NigelCleland)

  • improve documentation (@Brian-K-Smith)

Version 0.12.0 (2017-02-15)

  • rely on yahoo-finance to retrieve share information and share prices

  • use only ISO currency static data (remove support for looking on the web)

  • normalise post_date to 11:00AM

Version 0.11.0 (2016-11-13)

  • add support for python 3.5

  • add preload method on book to allow preloading all objects at once

Version 0.10.2 (2015-12-06)

  • add children argument to Account constructor

  • add a new example (used as answer to http://stackoverflow.com/questions/17055318/create-transaction-in-gnucash-in-response-to-an-email/ )

  • add a new example showing how to export Split information to pandas DataFrames

  • fix an error handling in retrieving currency exchanges in quandl

  • fix py3 bugs in dataframe functions

  • fix type and source of Pricers to be compatible with GnuCash

  • add a Price when entering a commodity Split

  • set microsecond to 0 for all datetime

  • add pandas for requirements-dev

  • add tests for deletion of transaction and for dataframe functions

Version 0.10.1 (2015-11-29)

  • refactor the validation mechanism to work well with autoflush=True

  • add support to GLIST in KVP

  • add new matching rule for GUID slots

  • rename slot ‘default_currency’ to ‘default-currency’

  • add tests for single_transaction factory

  • update ipython example with pandas dataframes

Version 0.10.0 (2015-11-18)

  • first draft of splits_df and prices_df methods that bring the book data into pandas DataFrames

  • add an ipython notebook to show the new dataframes methods

  • save default_currency of a book in a slot (when book created by piecash) or use locale to retrieve the default_currency

  • improve error handling for quandl queries (currency exchange rates)

Version 0.9.1 (2015-11-15)

  • fix bug with unicode on MySQL

Version 0.9.0 (2015-11-15)

  • ported to SQLAlchemy-1.0

  • set autoflush=true on the SA session

  • improved coverage above 90% for all modules

  • setup coveralls.io and requires.io

  • fix bugs discovered by improved testing

Version 0.8.4 (2015-11-14)

  • use AppVeyor for Windows continuous integration and for .exe freezing

  • fix bugs in tests suite where files were not properly closed

  • add Book.close function to close properly files

  • depend on enum-compat instead of directly enum34

  • add simple script to import/export prices from a gnucash book

Version 0.8.3 (2015-11-01)

  • fix issue #8 re enum34

  • updated sqlalchemy dep to use latest 0.9 series

Version 0.8.2 (2015-05-09)

  • implementing support for creating Customer, Vendor and Employee objects as well as taxtables

Version 0.8.1 (2015-05-03)

  • get 100% coverage on transaction module (except for scheduled transactions)

  • account.full_name returns now unicode string

Version 0.8.0 (2015-05-02)

  • get 100% coverage on book and account module

  • fix repr and str representations of all objects to be compatible py2 and py3

Version 0.7.6 (2015-05-01)

  • fix version requirement for SA (<0.9.9) and SA-utils

Version 0.7.5 (2015-03-14)

  • improve doc on installation on windows through conda

  • add .gitattributes to exclude html from githug language detection algorithm

  • update github project list

  • refactor sqlite isolation level code

  • fix setup.py to avoid sqlalchemy 0.9.9 (buggy version)

  • fix requirements.txt to avoid sqlalchemy 0.9.9 (buggy version)

Version 0.7.4 (2015-03-09)

  • remove some remaining print in code

Version 0.7.3 (2015-03-09)

  • fix requirements to include ipython==2.3.1

Version 0.7.2 (2015-03-09)

  • fix bug in doc (was using ledger_str instead of ledger)

Version 0.7.1 (2015-03-09)

  • refactor ledger functionalities

  • bug fixing

  • read backup functionality (ie backup when opening a book in RW)

Version 0.7.0 (2015-02-12)

  • Merge the GncSession and Book objects

  • extract factory function into a factories module

Version 0.6.2 (2015-02-02)

  • add reference to google groups

  • disable acquiring lock on file

Version 0.6.1 (2015-02-01)

  • fix: qif scripts was not included in package

Version 0.6.0 (2015-02-01)

  • add a basic QIF exporter script as piecash_toqif

  • implemented “Trading accounts”

  • improved documentation

  • other small api enhancements/changes

Version 0.5.11 (2015-01-12)

  • add a ledger_str method to transaction to output transaction in the ledger-cli format

  • add label to Decimal field in sqlalchemy expr

  • add backup option when opening sqlite file in RW (enabled by default)

  • renamed tx_guid to transaction_guid in Split field

  • fix technical bug in validation of transaction

Version 0.5.10 (2015-01-05)

  • add keywords to setup.py

Version 0.5.8 (2015-01-05)

  • add notes to Transaction (via slot)

  • removed standalone exe from git/package (as too large)

Version 0.5.7 (2015-01-04)

  • add sign property on account

  • raise NotImplementedError when creating an object is not “safe” (ie not __init__ and validators)

  • renamed slot_collection to slots in kvp handling

  • renamed field of Version + add explicit __init__

  • updated test to add explicit __init__ when needed

Version 0.5.6 (2015-01-04)

  • reordering of field definitions to match gnucash order (finished)

  • add autoincr

Version 0.5.5 (2015-01-04)

  • reordering of field definitions to match gnucash order (to complete)

Version 0.5.4 (2015-01-04)

  • added back the order table in the declarations

Version 0.5.3 (2015-01-03)

  • add support for schedule_transactions and lots (in terms of access to data, not business logic)

  • improved doc

Version 0.5.2 (2015-01-03)

  • reworked documentation

  • moved Lot and ScheduledTransaction to transaction module + improved them

  • improve slots support

  • fixed minor bugs

Version 0.5.1 (2014-12-30)

  • fixed changelog/what’s new documentation

Version 0.5.0 (2014-12-30)

  • improve relationship in business model

  • fix account.placeholder validation in transaction/splits

  • made all relationships dual (with back_populates instead of backref)

Version 0.4.4 (2014-12-28)

  • fix bug in piecash_ledger (remove testing code)

  • improve documentation of core objects

  • fix dependencies for developers (requests)

  • regenerate the github list of projects

Version 0.4.0 (2014-12-28)

  • improve bumpr integration

Version 0.3.1

  • renamed modules in piecash packages

  • updated doc

Version 0.3.0

  • ported to python 3.4

  • refactored lot of classes

  • improved documentation

  • added helper functions:

    • Commodity.create_currency_from_ISO()

    • Commodity.create_stock_from_symbol()

    • Commodity.update_prices()

    • Commodity.create_stock_accounts()

Contents:

Documentation

This project provides a simple and pythonic interface to GnuCash files stored in SQL (sqlite3, Pandostgres and MySQL) for Linux and Windows (not tested on Mac OS).

piecash is a pure python package, tested on python 3.6/3.7/3.8, that can be used as an alternative to:

  • the official python bindings (as long as no advanced book modifications and/or engine calculations are needed). This is specially useful on Windows where the official python bindings may be tricky to install or if you want to work with python 3.

  • XML parsing/reading of XML GnuCash files if you prefer python over XML/XLST manipulations.

piecash is built on the excellent SQLAlchemy library and does not require the installation of GnuCash itself.

piecash allows you to:

  • create a GnuCash book from scratch or edit an existing one

  • create new accounts, transactions, etc or change (within some limits) existing objects.

  • read/browse all objects through an intuitive interface

A simple example of a piecash script:

with open_book("example.gnucash") as book:
    # get default currency of book
    print( book.default_currency )  # ==> Commodity<CURRENCY:EUR>

    # iterating over all splits in all books and print the transaction description:
    for acc in book.accounts:
        for sp in acc.splits:
            print(sp.transaction.description)

As piecash is essentially a SQLAlchemy layer, it could be potentially reused by any web framework that has a SQLAlchemy interface to develop REST API or classical websites. It can also be used for reporting purposes.

The project has reached beta stage. Knowledge of SQLAlchemy is at this stage not anymore required to use it and/or to contribute to it. Some documentation for developers on the object model of GnuCash as understood by the author is available here.

Warning

  1. Always do a backup of your gnucash file/DB before using piecash.

  2. Test first your script by opening your file in readonly mode (which is the default mode)

Installation

To install with pip:

$ pip install piecash

or to upgrade if piecash is already installed:

$ pip install -U piecash
piecash comes with 6 extra options (each option depends on extra packages that will be installed only if the option is chosen):
For developers, two extra options:
  • test: to install what is needed for testing piecash

  • dev: to install what is needed for developing piecash (docs, …)

To install these options, simply specify them between brackets after the piecash package:

$ pip install piecash[pandas,qif,postgres]

To install with pipenv:

$ pipenv install piecash

Otherwise, you can install by unpacking the source distribution from PyPI and then:

$ python setup.py install

If you are on MS Windows and not so familiar with python, we would suggest you to install the miniconda python distribution from Continuum Analytics available at http://conda.pydata.org/miniconda.html (you can choose whatever version 3.X of python you would like) and then run the following command in the command prompt (cmd.exe):

$ conda create -n piecash_venv python=3 pip sqlalchemy
$ activate piecash_venv
$ pip install piecash

The first command create a new python environment named “piecash_venv” with python 3.7, pip and sqlalchemy installed.

The second command activates the newly created piecash_venv. Afterwards, you only need to execute this command before using python through the command line.

The third command installs piecash and its dependencies. piecash depends also on sqlalchemy but as the sqlalchemy package requires a compiler if it is installed through pip, we found it easier to install it through conda (this is done in the first command).

If you need to use directly the python interpreter in the newly created “piecash_env”, you can find it installed in your user folder under Miniconda3\envs\piecash_venv\python.exe (or Miniconda2\…).

On OS X, this option may also be valuable.

Quickstart

The simplest workflow to use piecash starts by opening a GnuCash file

import piecash

# open a GnuCash Book
book = piecash.open_book("test.gnucash", readonly=True)

and then access GnuCash objects through the book, for example to query the stock prices

# example 1, print all stock prices in the Book
# display all prices
for price in book.prices:
    print(price)
<Price 2014-12-22 : 0.702755 EUR/CAD>
<Price 2014-12-19 : 0.695658 EUR/CAD>
<Price 2014-12-18 : 0.689026 EUR/CAD>
<Price 2014-12-17 : 0.69005 EUR/CAD>
<Price 2014-12-16 : 0.693247 EUR/CAD>
<Price 2014-12-22 : 51.15 USD/YHOO>
<Price 2014-12-19 : 50.88 USD/YHOO>
<Price 2014-12-18 : 50.91 USD/YHOO>
<Price 2014-12-17 : 50.12 USD/YHOO>
<Price 2014-12-16 : 48.85 USD/YHOO>
...

or to query the accounts:

for account in book.accounts:
    print(account)
Account<[EUR]>
Account<Assets[EUR]>
Account<Assets:Current Assets[EUR]>
Account<Assets:Current Assets:Checking Account[EUR]>
Account<Assets:Current Assets:Savings Account[EUR]>
Account<Assets:Current Assets:Cash in Wallet[EUR]>
Account<Income[EUR]>
Account<Income:Bonus[EUR]>
Account<Income:Gifts Received[EUR]>
...
Account<Expenses[EUR]>
Account<Expenses:Commissions[EUR]>
Account<Expenses:Adjustment[EUR]>
Account<Expenses:Auto[EUR]>
Account<Expenses:Auto:Fees[EUR]>
...
Account<Liabilities[EUR]>
Account<Liabilities:Credit Card[EUR]>
Account<Equity[EUR]>
Account<Equity:Opening Balances[EUR]>
...

or to create a new expense account for utilities:

# retrieve currency
EUR = book.commodities.get(mnemonic='EUR')

# retrieve parent account
acc_exp = book.accounts.get(fullname="Expenses:Utilities")

# add a new subaccount to this account of type EXPENSE with currency EUR
new_acc = piecash.Account(name="Cable", type="EXPENSE", parent=acc_exp, commodity=EUR)

# save changes (it should raise an exception if we opened the book as readonly)
book.save()

Most basic objects used for personal finance are supported (Account, Split, Transaction, Price, …).

The piecash command line interface

The piecash CLI offers the following features:

$ piecash -h
Usage: piecash [OPTIONS] COMMAND [ARGS]...

Options:
  -h, --help  Show this message and exit.

Commands:
  export      Exports GnuCash ENTITIES.
  ledger      Export to ledger-cli format.
  qif         Export to QIF format.
  sql-create  Create an empty book with gnucash
  sql-dump    Dump SQL schema of the gnucash sqlite book

To export specific entities out of a GnuCash book:

$ piecash export -h
Usage: piecash export [OPTIONS] BOOK [customers|vendors|prices]

  Exports GnuCash ENTITIES.

  This scripts export ENTITIES from the BOOK in a CSV format. When possible,
  it exports in a format that can be used to import the data into GnuCash.

  Remarks:
  - for customers and vendors, the format does not include an header
  - for prices, the format can be used with the `piecash import` command.

Options:
  --output FILENAME  File to which to export the data (default=stdout)
  --inactive         Include inactive entities (for vendors and customers)
  -h, --help         Show this message and exit.

To export a GnuCash book to the ledger-cli format:

$ piecash ledger -h
Usage: piecash ledger [OPTIONS] BOOK

  Export to ledger-cli format.

  This scripts export a GnuCash BOOK to the ledget-cli format.

Options:
  --locale / --no-locale          Export currency amounts using locale for
                                  currencies format

  --commodity-notes / --no-commodity-notes
                                  Include the commodity_notes for the
                                  commodity (hledger does not support
                                  commodity commodity_notes

  --short-account-names / --no-short-account-names
                                  Use the short name for the accounts instead
                                  of the full hierarchical name.

  --output FILENAME               File to which to export the data
                                  (default=stdout)

  -h, --help                      Show this message and exit.

Or in python

In [1]: book = open_book(gnucash_books + "simple_sample.gnucash", open_if_lock=True)

In [2]: from piecash import ledger

# printing the ledger-cli (https://www.ledger-cli.org/) representation of the book
In [3]: print(ledger(book))
commodity EUR

account Asset
	check commodity == "EUR"

account Liability
	check commodity == "EUR"

account Income
	check commodity == "EUR"

account Expense
	check commodity == "EUR"

account Equity
	check commodity == "EUR"

account Equity:Opening Balances - EUR
	check commodity == "EUR"


2014-11-30 Opening Balance
	Equity:Opening Balances - EUR             EUR -500.00
	Asset                                     EUR 500.00

2014-12-24 initial load
	Liability                                 EUR -1,000.00
	Asset                                     EUR 1,000.00

2014-12-24 expense 1
	Asset                                     EUR -200.00
	Expense                                   EUR 200.00

2014-12-24 income 1
	Income                                    EUR -150.00
	Asset                                     EUR 150.00

2014-12-24 loan payment
	Asset                                     EUR -130.00 ;   monthly payment     
	Expense                                   EUR 30.00 ;   interest            
	Liability                                 EUR 100.00 ;   capital             



# printing the ledger-cli (https://www.ledger-cli.org/) representation of the book using regional settings (locale) for currency output
In [4]: print(ledger(book, locale=True))
commodity €

account Asset
	check commodity == "EUR"

account Liability
	check commodity == "EUR"

account Income
	check commodity == "EUR"

account Expense
	check commodity == "EUR"

account Equity
	check commodity == "EUR"

account Equity:Opening Balances - EUR
	check commodity == "EUR"


2014-11-30 Opening Balance
	Equity:Opening Balances - EUR             -€500.00
	Asset                                     €500.00

2014-12-24 initial load
	Liability                                 -€1,000.00
	Asset                                     €1,000.00

2014-12-24 expense 1
	Asset                                     -€200.00
	Expense                                   €200.00

2014-12-24 income 1
	Income                                    -€150.00
	Asset                                     €150.00

2014-12-24 loan payment
	Asset                                     -€130.00 ;   monthly payment     
	Expense                                   €30.00 ;   interest            
	Liability                                 €100.00 ;   capital             

For more information on how to use piecash, please refer to the Tutorials on Using existing objects and Creating new objects, the Example scripts or the package documentation.

Tutorial : using existing objects

Opening an existing Book

To open an existing GnuCash document (and get the related Book), use the open_book() function:

import piecash

# for a sqlite3 document
book = piecash.open_book("existing_file.gnucash")

# or through an URI connection string for sqlite3
book = piecash.open_book(uri_conn="sqlite:///existing_file.gnucash")
# or for postgres
book = piecash.open_book(uri_conn="postgres://user:passwd@localhost/existing_gnucash_db")

The documents are open as readonly per default. To allow RW access, specify explicitly readonly=False as:

book = piecash.open_book("existing_file.gnucash", readonly=False)

When opening in full access (readonly=False), piecash will automatically create a backup file named filename.piecash_YYYYMMDD_HHMMSS with the original file. To avoid creating the backup file, specificy do_backup=False as:

book = piecash.open_book("existing_file.gnucash", readonly=False, do_backup=False)

To force opening the file even through there is a lock on it, use the open_if_lock=True argument:

book = piecash.open_book("existing_file.gnucash", open_if_lock=True)

Access to objects

Once a GnuCash book is opened through a piecash.core.book.Book, GnuCash objects can be accessed through two different patterns:

The object model

In this mode, we access elements through their natural relations, starting from the book and jumping from one object to the other:

In [1]: book = open_book(gnucash_books + "default_book.gnucash")

In [2]: book.root_account # accessing the root_account
Out[2]: Account<[EUR]>

In [3]: # looping through the children accounts of the root_account
   ...: for acc in book.root_account.children:
   ...:     print(acc)
   ...: 
Account<Assets[EUR]>
Account<Liabilities[EUR]>
Account<Income[EUR]>
Account<Expenses[EUR]>
Account<Equity[EUR]>

# accessing children accounts
In [4]: 
   ...: root = book.root_account              # select the root_account
   ...: assets = root.children(name="Assets")   # select child account by name
   ...: cur_assets = assets.children[0]         # select child account by index
   ...: cash = cur_assets.children(type="CASH") # select child account by type
   ...: print(cash)
   ...: 
Account<Assets:Current Assets:Cash in Wallet[EUR]>

In [5]: # get the commodity of an account
   ...: commo = cash.commodity
   ...: print(commo)
   ...: 
Commodity<CURRENCY:EUR>

In [6]: # get first ten accounts linked to the commodity commo
   ...: for acc in commo.accounts[:10]:
   ...:     print(acc)
   ...: 
Account<[EUR]>
Account<Assets[EUR]>
Account<Assets:Current Assets[EUR]>
Account<Assets:Current Assets:Checking Account[EUR]>
Account<Assets:Current Assets:Savings Account[EUR]>
Account<Assets:Current Assets:Cash in Wallet[EUR]>
Account<Liabilities[EUR]>
Account<Liabilities:Credit Card[EUR]>
Account<Income[EUR]>
Account<Income:Bonus[EUR]>

The “table” access

In this mode, we access elements through collections directly accessible from the book:

In [7]: book = open_book(gnucash_books + "default_book.gnucash")

# accessing all accounts
In [8]: book.accounts
Out[8]: 
[Account<Assets[EUR]>,
 Account<Assets:Current Assets[EUR]>,
 Account<Assets:Current Assets:Checking Account[EUR]>,
 Account<Assets:Current Assets:Savings Account[EUR]>,
 Account<Assets:Current Assets:Cash in Wallet[EUR]>,
 Account<Liabilities[EUR]>,
 Account<Liabilities:Credit Card[EUR]>,
 Account<Income[EUR]>,
 Account<Income:Bonus[EUR]>,
 Account<Income:Gifts Received[EUR]>,
 Account<Income:Interest Income[EUR]>,
 Account<Income:Interest Income:Checking Interest[EUR]>,
 Account<Income:Interest Income:Other Interest[EUR]>,
 Account<Income:Interest Income:Savings Interest[EUR]>,
 Account<Income:Other Income[EUR]>,
 Account<Income:Salary[EUR]>,
 Account<Expenses[EUR]>,
 Account<Expenses:Adjustment[EUR]>,
 Account<Expenses:Auto[EUR]>,
 Account<Expenses:Auto:Fees[EUR]>,
 Account<Expenses:Auto:Gas[EUR]>,
 Account<Expenses:Auto:Parking[EUR]>,
 Account<Expenses:Auto:Repair and Maintenance[EUR]>,
 Account<Expenses:Bank Service Charge[EUR]>,
 Account<Expenses:Books[EUR]>,
 Account<Expenses:Cable[EUR]>,
 Account<Expenses:Charity[EUR]>,
 Account<Expenses:Clothes[EUR]>,
 Account<Expenses:Computer[EUR]>,
 Account<Expenses:Dining[EUR]>,
 Account<Expenses:Education[EUR]>,
 Account<Expenses:Entertainment[EUR]>,
 Account<Expenses:Entertainment:Music/Movies[EUR]>,
 Account<Expenses:Entertainment:Recreation[EUR]>,
 Account<Expenses:Entertainment:Travel[EUR]>,
 Account<Expenses:Gifts[EUR]>,
 Account<Expenses:Groceries[EUR]>,
 Account<Expenses:Hobbies[EUR]>,
 Account<Expenses:Insurance[EUR]>,
 Account<Expenses:Insurance:Auto Insurance[EUR]>,
 Account<Expenses:Insurance:Health Insurance[EUR]>,
 Account<Expenses:Insurance:Life Insurance[EUR]>,
 Account<Expenses:Laundry/Dry Cleaning[EUR]>,
 Account<Expenses:Medical Expenses[EUR]>,
 Account<Expenses:Miscellaneous[EUR]>,
 Account<Expenses:Online Services[EUR]>,
 Account<Expenses:Phone[EUR]>,
 Account<Expenses:Public Transportation[EUR]>,
 Account<Expenses:Subscriptions[EUR]>,
 Account<Expenses:Supplies[EUR]>,
 Account<Expenses:Taxes[EUR]>,
 Account<Expenses:Taxes:Federal[EUR]>,
 Account<Expenses:Taxes:Medicare[EUR]>,
 Account<Expenses:Taxes:Other Tax[EUR]>,
 Account<Expenses:Taxes:Social Security[EUR]>,
 Account<Expenses:Taxes:State/Province[EUR]>,
 Account<Expenses:Utilities[EUR]>,
 Account<Expenses:Utilities:Electric[EUR]>,
 Account<Expenses:Utilities:Garbage collection[EUR]>,
 Account<Expenses:Utilities:Gas[EUR]>,
 Account<Expenses:Utilities:Water[EUR]>,
 Account<Equity[EUR]>,
 Account<Equity:Opening Balances[EUR]>]

# accessing all commodities
In [9]: book.commodities
Out[9]: [Commodity<CURRENCY:EUR>]

# accessing all transactions
In [10]: book.transactions
Out[10]: []

Each of these collections can be either iterated or accessed through some indexation or filter mechanism (return first element of collection satisfying some criteria(s)):

# iteration
In [11]: for acc in book.accounts:
   ....:     if acc.type == "ASSET": print(acc)
   ....: 
Account<Assets[EUR]>
Account<Assets:Current Assets[EUR]>

# indexation (not very meaningful)
In [12]: book.accounts[10]
Out[12]: Account<Income:Interest Income[EUR]>

# filter by name
In [13]: book.accounts(name="Garbage collection")
Out[13]: Account<Expenses:Utilities:Garbage collection[EUR]>

# filter by type
In [14]: book.accounts(type="EXPENSE")
Out[14]: Account<Expenses[EUR]>

# filter by fullname
In [15]: book.accounts(fullname="Expenses:Taxes:Social Security")
Out[15]: Account<Expenses:Taxes:Social Security[EUR]>

# filter by multiple criteria
In [16]: book.accounts(commodity=book.commodities[0], name="Gas")
Out[16]: Account<Expenses:Auto:Gas[EUR]>

The “SQLAlchemy” access (advanced users)

In this mode, we access elements through SQLAlchemy queries on the SQLAlchemy session:

# retrieve underlying SQLAlchemy session object
In [1]: session = book.session

# get all account with name >= "T"
In [2]: session.query(Account).filter(Account.name>="T").all()
Out[2]: 
[Account<Expenses:Entertainment:Travel[EUR]>,
 Account<Expenses:Taxes[EUR]>,
 Account<Expenses:Utilities[EUR]>,
 Account<Expenses:Utilities:Water[EUR]>,
 Account<>]

# display underlying query
In [3]: str(session.query(Account).filter(Account.name>="T"))
Out[3]: 'SELECT accounts.account_type AS accounts_account_type, accounts.commodity_scu AS accounts_commodity_scu, accounts.non_std_scu AS accounts_non_std_scu, accounts.placeholder AS accounts_placeholder, accounts.guid AS accounts_guid, accounts.name AS accounts_name, accounts.commodity_guid AS accounts_commodity_guid, accounts.parent_guid AS accounts_parent_guid, accounts.code AS accounts_code, accounts.description AS accounts_description, accounts.hidden AS accounts_hidden \nFROM accounts \nWHERE accounts.name >= ?'

Accounts

Accessing the accounts (piecash.core.account.Account):

In [1]: book = open_book(gnucash_books + "simple_sample.gnucash", open_if_lock=True)

# accessing the root_account
In [2]: root = book.root_account

In [3]: print(root)
Account<>

# accessing the first children account of a book
In [4]: acc = root.children[0]

In [5]: print(acc)
Account<Asset[EUR]>

# accessing attributes of an account
In [6]: print(f"Account name={acc.name}\n"
   ...:       f"        commodity={acc.commodity.namespace}/{acc.commodity.mnemonic}\n"
   ...:       f"        fullname={acc.fullname}\n"
   ...:       f"        type={acc.type}")
   ...: 
Account name=Asset
        commodity=CURRENCY/EUR
        fullname=Asset
        type=ASSET

# calculating the balance of the accounts:
In [7]: for acc in root.children:
   ...:     print(f"Account balance for {acc.name}: {acc.get_balance()} (without sign reversal: {acc.get_balance(natural_sign=False)}")
   ...: 
Account balance for Asset: 1320 (without sign reversal: 1320
Account balance for Liability: 900 (without sign reversal: -900
Account balance for Income: 150 (without sign reversal: -150
Account balance for Expense: 230 (without sign reversal: 230
Account balance for Equity: 500 (without sign reversal: -500

# accessing all splits related to an account
In [8]: for sp in acc.splits:
   ...:     print(f"account <{acc.fullname}> is involved in transaction '{sp.transaction.description}'")
   ...: 

Commodities and Prices

The list of all commodities in the book can be retrieved via the commodities attribute:

In [1]: book = open_book(gnucash_books + "book_prices.gnucash", open_if_lock=True)

# all commodities
In [2]: print(book.commodities)
[Commodity<CURRENCY:EUR>, Commodity<NASDAQ:FB>, Commodity<template:template>, Commodity<PAR:ENGI.PA>, Commodity<NYQ:KO>, Commodity<CURRENCY:USD>]

In [3]: cdty = book.commodities[0]

# accessing attributes of a commodity
In [4]: print("Commodity namespace={cdty.namespace}\n"
   ...:       "          mnemonic={cdty.mnemonic}\n"
   ...:       "          cusip={cdty.cusip}\n"
   ...:       "          fraction={cdty.fraction}".format(cdty=cdty))
   ...: 
Commodity namespace=CURRENCY
          mnemonic=EUR
          cusip=978
          fraction=100

The prices (piecash.core.commodity.Price) of a commodity can be iterated through the prices attribute:

# loop on the prices
In [1]: for cdty in book.commodities:
   ...:     for pr in cdty.prices:
   ...:         print("Price date={pr.date}"
   ...:               "      value={pr.value} {pr.currency.mnemonic}/{pr.commodity.mnemonic}".format(pr=pr))
   ...: 
Price date=2018-02-10      value=1.730120457024597578303288749E-7 EUR/FB
Price date=2018-02-11      value=54 EUR/FB
Price date=2018-02-13      value=3.808073 EUR/FB
Price date=2018-02-12      value=5 EUR/FB
Price date=2018-02-15      value=3.923077 EUR/FB

Transactions and Splits

The list of all transactions in the book can be retrieved via the transactions attribute:

In [1]: book = open_book(gnucash_books + "book_schtx.gnucash", open_if_lock=True)

# all transactions (including transactions part of a scheduled transaction description)
In [2]: for tr in book.transactions:
   ...:     print(tr)
   ...: 
Transaction<[EUR] 'Monthly utility bill' on 2015-01-02>
Transaction<[EUR] 'Insurance' on 2015-01-02>
Transaction<[EUR] 'Monthly utility bill' on 2013-12-31 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2014-02-02 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2014-03-02 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2014-03-31 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2014-04-30 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2014-06-01 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2014-06-30 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2014-07-31 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2014-08-31 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2014-09-30 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2014-11-02 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2014-11-30 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2014-12-31 (from sch tx)>
Transaction<[EUR] 'Insurance' on 2013-05-31 (from sch tx)>
Transaction<[EUR] 'Insurance' on 2014-05-29 (from sch tx)>
Transaction<[EUR] 'Opening balance' on 2013-01-02>
Transaction<[EUR] 'Monthly utility bill' on 2015-02-01 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2015-03-01 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2015-03-31 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2015-04-30 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2015-05-31 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2015-06-30 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2015-08-02 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2015-08-31 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2015-09-30 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2015-11-01 (from sch tx)>
Transaction<[EUR] 'Insurance' on 2015-05-31 (from sch tx)>
Transaction<[EUR] 'test' on 2015-11-17>
Transaction<[EUR] 'salary' on 2013-12-31>
Transaction<[EUR] 'salary' on 2014-12-31>
Transaction<[EUR] 'Monthly utility bill' on 2015-12-01 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2016-01-01 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2016-02-01 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2016-03-01 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2016-04-01 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2016-05-02 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2016-06-01 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2016-07-01 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2016-08-01 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2016-09-01 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2016-10-03 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2016-11-01 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2016-12-01 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2017-01-02 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2017-02-01 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2017-03-01 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2017-04-03 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2017-05-01 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2017-06-01 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2017-07-03 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2017-08-01 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2017-09-01 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2017-10-02 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2017-11-01 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2017-12-01 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2018-01-01 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2018-02-01 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2018-03-01 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2018-04-02 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2018-05-01 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2018-06-01 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2018-07-02 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2018-08-01 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2018-09-03 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2018-10-01 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2018-11-01 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2018-12-03 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2019-01-01 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2019-02-01 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2019-03-01 (from sch tx)>
Transaction<[EUR] 'Monthly utility bill' on 2019-04-01 (from sch tx)>
Transaction<[EUR] 'Insurance' on 2016-06-01 (from sch tx)>
Transaction<[EUR] 'Insurance' on 2017-06-01 (from sch tx)>
Transaction<[EUR] 'Insurance' on 2018-06-01 (from sch tx)>

# selecting first transaction generated from a scheduled transaction
In [3]: tr = [ tr for tr in book.transactions if tr.scheduled_transaction ][0]

For a given transaction, the following attributes are accessible:

# accessing attributes of a transaction
In [1]: print("Transaction description='{tr.description}'\n"
   ...:       "            currency={tr.currency}\n"
   ...:       "            post_date={tr.post_date}\n"
   ...:       "            enter_date={tr.enter_date}".format(tr=tr))
   ...: 
Transaction description='Monthly utility bill'
            currency=Commodity<CURRENCY:EUR>
            post_date=2013-12-31
            enter_date=2015-01-03 08:18:13+00:00

# accessing the splits of the transaction
In [2]: tr.splits
Out[2]: 
[Split<Account<Assets:Current Assets:Checking Account[EUR]> -70 EUR>,
 Split<Account<Expenses:Utilities:Electric[EUR]> 30 EUR>,
 Split<Account<Expenses:Utilities:Gas[EUR]> 40 EUR>]

# identifying which split is a credit or a debit
In [3]: for sp in tr.splits:
   ...:     split_type = "credit" if sp.is_credit else "debit"
   ...:     print(f"{sp} is a {split_type}")
   ...: 
Split<Account<Assets:Current Assets:Checking Account[EUR]> -70 EUR> is a credit
Split<Account<Expenses:Utilities:Electric[EUR]> 30 EUR> is a debit
Split<Account<Expenses:Utilities:Gas[EUR]> 40 EUR> is a debit

# accessing the scheduled transaction
In [4]: [ sp for sp in tr.scheduled_transaction.template_account.splits]
Out[4]: 
[SplitTemplate<Account<Assets:Current Assets:Checking Account[EUR]> credit=70 >,
 SplitTemplate<Account<Expenses:Utilities:Electric[EUR]>  debit=30>,
 SplitTemplate<Account<Expenses:Utilities:Gas[EUR]>  debit=40>]

# closing the book
In [5]: book.close()

Invoices

The list of all invoices in the book can be retrieved via the invoices attribute:

In [1]: book = open_book(gnucash_books + "invoices.gnucash", open_if_lock=True)

# all invoices
In [2]: for invoice in book.invoices:
   ...:     print(invoice)
   ...: 
Invoice<000001>

Other objects

In fact, any object can be retrieved from the session through a generic get(**kwargs) method:

In [1]: book = open_book(gnucash_books + "invoices.gnucash", open_if_lock=True)

In [2]: from piecash import Account, Commodity, Budget, Vendor

# accessing specific objects through the get method
In [3]: book.get(Account, name="Assets", parent=book.root_account)
Out[3]: Account<Assets[EUR]>

In [4]: book.get(Commodity, namespace="CURRENCY", mnemonic="EUR")
Out[4]: Commodity<CURRENCY:EUR>

In [5]: book.get(Budget, name="my first budget")
Out[5]: Budget<my first budget() for 12 periods following pattern 'month*1 from 2019-04-01 [none]' >

In [6]: book.get(Vendor, name="Looney")
Out[6]: Vendor<000001:Looney>

If you know SQLAlchemy, you can get access to the underlying Session as book.session and execute queries using the piecash classes:

In [7]: from piecash import Account, Commodity, Budget, Vendor

# get the SQLAlchemy session
In [8]: session = book.session

# loop through all invoices
In [9]: for invoice in session.query(Invoice).all():
   ...:     print(invoice.notes)
   ...: 

Note

Easy access to objects from piecash.business and piecash.budget could be given directly from the session in future versions if deemed useful.

Working with slots

With regard to slots, GnuCash objects and Frames behave as dictionaries and all values are automatically converted back and forth to python objects:

In [1]: import datetime, decimal

In [2]: book = create_book()

# retrieve list of slots
In [3]: print(book.slots)
[]

# set slots
In [4]: book["myintkey"] = 3

In [5]: book["mystrkey"] = "hello"

In [6]: book["myboolkey"] = True

In [7]: book["mydatekey"] = datetime.datetime.today().date()

In [8]: book["mydatetimekey"] = datetime.datetime.today()

In [9]: book["mynumerickey"] = decimal.Decimal("12.34567")

In [10]: book["account"] = book.root_account

# iterate over all slots
In [11]: for k, v in book.iteritems():
   ....:     print("slot={v} has key={k} and value={v.value} of type {t}".format(k=k,v=v,t=type(v.value)))
   ....: 
slot=<SlotInt myintkey=3> has key=myintkey and value=3 of type <class 'int'>
slot=<SlotString mystrkey='hello'> has key=mystrkey and value=hello of type <class 'str'>
slot=<SlotInt myboolkey=True> has key=myboolkey and value=True of type <class 'bool'>
slot=<SlotDate mydatekey=datetime.date(2021, 10, 19)> has key=mydatekey and value=2021-10-19 of type <class 'datetime.date'>
slot=<SlotTime mydatetimekey=datetime.datetime(2021, 10, 19, 8, 49, 58, 312301)> has key=mydatetimekey and value=2021-10-19 08:49:58.312301 of type <class 'datetime.datetime'>
slot=<SlotNumeric mynumerickey=Decimal('12.34567')> has key=mynumerickey and value=12.34567 of type <class 'decimal.Decimal'>
slot=<SlotGUID account=Account<[EUR]>> has key=account and value=Account<[EUR]> of type <class 'piecash.core.account.Account'>

# delete a slot
In [12]: del book["myintkey"]

# delete all slots
In [13]: del book[:]

# create a key/value in a slot frames (and create them if they do not exist)
In [14]: book["options/Accounts/Use trading accounts"]="t"

# access a slot in frame in whatever notations
In [15]: s1=book["options/Accounts/Use trading accounts"]

In [16]: s2=book["options"]["Accounts/Use trading accounts"]

In [17]: s3=book["options/Accounts"]["Use trading accounts"]

In [18]: s4=book["options"]["Accounts"]["Use trading accounts"]

In [19]: assert s1==s2==s3==s4

Slots of type GUID use the name of the slot to do the conversion back and forth between an object and its guid. For these slots, there is an explicit mapping between slot names and object types.

Tutorial : creating new objects

Creating a new Book

piecash can create a new GnuCash document (a Book) from scratch through the create_book() function.

To create a in-memory sqlite3 document (useful to test piecash for instance), a simple call is enough:

In [1]: import piecash

In [2]: book = piecash.create_book()

To create a file-based sqlite3 document:

In [3]: book = piecash.create_book("example_file.gnucash")

# or equivalently (adding the overwrite=True argument to overwrite the file if it already exists)
In [4]: book = piecash.create_book(sqlite_file="example_file.gnucash", overwrite=True)

# or equivalently
In [5]: book = piecash.create_book(uri_conn="sqlite:///example_file.gnucash", overwrite=True)

and for a postgres document (needs the psycopg2 package installable via “pip install psycopg2”):

book = piecash.create_book(uri_conn="postgres://user:passwd@localhost/example_gnucash_db")

Note

Per default, the currency of the document is the euro (EUR) but you can specify any other ISO currency through its ISO symbol:

In [6]: book = piecash.create_book(sqlite_file="example_file.gnucash",
   ...:                         currency="USD",
   ...:                         overwrite=True)
   ...: 

If the document already exists, piecash will raise an exception. You can force piecash to overwrite an existing file/database (i.e. delete it and then recreate it) by passing the overwrite=True argument:

In [1]: book = piecash.create_book(sqlite_file="example_file.gnucash", overwrite=True)

Creating a new Account

piecash can create new accounts (a piecash.core.account.Account):

In [1]: from piecash import create_book, Account

In [2]: book = create_book(currency="EUR")

# retrieve the default currency
In [3]: EUR = book.commodities.get(mnemonic="EUR")

# creating a placeholder account
In [4]: acc = Account(name="My account",
   ...:               type="ASSET",
   ...:               parent=book.root_account,
   ...:               commodity=EUR,
   ...:               placeholder=True,)
   ...: 

# creating a detailed sub-account
In [5]: subacc = Account(name="My sub account",
   ...:                  type="BANK",
   ...:                  parent=acc,
   ...:                  commodity=EUR,
   ...:                  commodity_scu=1000,
   ...:                  description="my bank account",
   ...:                  code="FR013334...",)
   ...: 

In [6]: book.save()

In [7]: book.accounts
Out[7]: [Account<My account[EUR]>, Account<My account:My sub account[EUR]>]

Creating a new Commodity

piecash can create new commodities (a piecash.core.commodity.Commodity):

In [1]: from piecash import create_book, Commodity, factories

# create a book (in memory) with some currency
In [2]: book = create_book(currency="EUR")

In [3]: print(book.commodities)
[Commodity<CURRENCY:EUR>]

# creating a new ISO currency (if not already available in s.commodities) (warning, object should be manually added to session)
In [4]: USD = factories.create_currency_from_ISO("USD")

In [5]: book.add(USD) # add to session

# create a commodity (lookup on yahoo! finance, need web access)
# (warning, object should be manually added to session if book kwarg is not included in constructor)
# DOES NOT WORK ANYMORE DUE TO CLOSING OF YAHOO!FINANCE
# apple = factories.create_stock_from_symbol("AAPL", book)
# creating commodities using the constructor
# (warning, object should be manually added to session if book kwarg is not included in constructor)
# create a special "reward miles" Commodity using the constructor without book kwarg
In [6]: miles = Commodity(namespace="LOYALTY", mnemonic="Miles", fullname="Reward miles", fraction=1000000)

In [7]: book.add(miles) # add to session

# create a special "unicorn hugs" Commodity using the constructor with book kwarg
In [8]: unhugs = Commodity(namespace="KINDNESS", mnemonic="Unhugs", fullname="Unicorn hugs", fraction=1, book=book)

In [9]: USD, miles, unhugs
Out[9]: (Commodity<CURRENCY:USD>, Commodity<LOYALTY:Miles>, Commodity<KINDNESS:Unhugs>)

Warning

The following (creation of non ISO currencies) is explicitly forbidden by the GnuCash application.

# create a bitcoin currency (warning, max 6 digits after comma, current GnuCash limitation)
In [1]: XBT = Commodity(namespace="CURRENCY", mnemonic="XBT", fullname="Bitcoin", fraction=1000000)

In [2]: book.add(XBT) # add to session

In [3]: XBT
Out[3]: Commodity<CURRENCY:XBT>

Creating a new Transaction

piecash can create new transactions (a piecash.core.transaction.Transaction):

In [1]: from piecash import create_book, Account, Transaction, Split, GncImbalanceError, factories, ledger

# create a book (in memory)
In [2]: book = create_book(currency="EUR")

# get the EUR and create the USD currencies
In [3]: c1 = book.default_currency

In [4]: c2 = factories.create_currency_from_ISO("USD")

# create two accounts
In [5]: a1 = Account("Acc 1", "ASSET", c1, parent=book.root_account)

In [6]: a2 = Account("Acc 2", "ASSET", c2, parent=book.root_account)

# create a transaction from a1 to a2
In [7]: tr = Transaction(currency=c1,
   ...:                  description="transfer",
   ...:                  splits=[
   ...:                      Split(account=a1, value=-100),
   ...:                      Split(account=a2, value=100, quantity=30)
   ...:                  ])
   ...: 

In [8]: book.flush()

# ledger() returns a representation of the transaction in the ledger-cli format
In [9]: print(ledger(tr))
2021-10-19 transfer
	Acc 1                                     EUR -100.00
	Acc 2                                     USD 30.00 @@ EUR 100.00


# change the book to use the "trading accounts" options
In [10]: book.use_trading_accounts = True

# add a new transaction identical to the previous
In [11]: tr2 = Transaction(currency=c1,
   ....:                   description="transfer 2",
   ....:                   splits=[
   ....:                       Split(account=a1, value=-100),
   ....:                       Split(account=a2, value=100, quantity=30)
   ....:                   ])
   ....: 

In [12]: print(ledger(tr2))
2021-10-19 transfer 2
	Acc 1                                     EUR -100.00
	Acc 2                                     USD 30.00 @@ EUR 100.00


# when flushing, the trading accounts are created
In [13]: book.flush()

In [14]: print(ledger(tr2))
2021-10-19 transfer 2
	Acc 1                                     EUR -100.00
	Acc 2                                     USD 30.00 @@ EUR 100.00


# trying to create an unbalanced transaction trigger an exception
# (there is not automatic creation of an imbalance split)
In [15]: tr3 = Transaction(currency=c1,
   ....:                   description="transfer imb",
   ....:                   splits=[
   ....:                       Split(account=a1, value=-100),
   ....:                       Split(account=a2, value=90, quantity=30)
   ....:                   ])
   ....: 

In [16]: print(ledger(tr3))
2021-10-19 transfer imb
	Acc 1                                     EUR -100.00
	Acc 2                                     USD 30.00 @@ EUR 90.00


In [17]: try:
   ....:     book.flush()
   ....: except GncImbalanceError:
   ....:     print("Indeed, there is an imbalance !")
   ....: 

Creating new Business objects

piecash can create new ‘business’ objects (this is a work in progress).

To create a new customer (a piecash.business.person.Customer):

In [1]: from piecash import create_book, Customer, Address

# create a book (in memory)
In [2]: b = create_book(currency="EUR")

# get the currency
In [3]: eur = b.default_currency

# create a customer
In [4]: c1 = Customer(name="Mickey", currency=eur, address=Address(addr1="Sesame street 1", email="mickey@example.com"))

# the customer has not yet an ID
In [5]: c1
Out[5]: Customer<None:Mickey>

# we add it to the book
In [6]: b.add(c1)

# flush the book
In [7]: b.flush()

# the customer gets its ID
In [8]: print(c1)
Customer<000001:Mickey>

# or create a customer directly in a book (by specifying the book argument)
In [9]: c2 = Customer(name="Mickey", currency=eur, address=Address(addr1="Sesame street 1", email="mickey@example.com"),
   ...:               book=b)
   ...: 

# the customer gets immediately its ID
In [10]: c2
Out[10]: Customer<000002:Mickey>

# the counter of the ID is accessible as
In [11]: b.counter_customer
Out[11]: 2

In [12]: b.save()

Similar functions are available to create new vendors (piecash.business.person.Vendor) or employees (piecash.business.person.Employee).

There is also the possibility to set taxtables for customers or vendors as:

In [1]: from piecash import Taxtable, TaxtableEntry

In [2]: from decimal import Decimal

# let us first create an account to which link a tax table entry
In [3]: acc = Account(name="MyTaxAcc", parent=b.root_account, commodity=b.currencies(mnemonic="EUR"), type="ASSET")

# then create a table with on entry (6.5% on previous account
In [4]: tt = Taxtable(name="local taxes", entries=[
   ...:     TaxtableEntry(type="percentage",
   ...:                   amount=Decimal("6.5"),
   ...:                   account=acc),
   ...: ])
   ...: 

# and finally attach it to a customer
In [5]: c2.taxtable = tt

In [6]: b.save()

In [7]: print(b.taxtables)
[TaxTable<local taxes:['TaxEntry<6.5 percentage in MyTaxAcc>']>]

Examples of programs written with piecash

You can find examples of programs/scripts (loosely based on the scripts for the official python bindings for gnucash or on questions posted on the mailing list) in the examples subfolder.

Creating and opening gnucash files

from __future__ import print_function
import os
import tempfile

from piecash import open_book, create_book, GnucashException


FILE_1 = os.path.join(tempfile.gettempdir(), "not_there.gnucash")
FILE_2 = os.path.join(tempfile.gettempdir(), "example_file.gnucash")

if os.path.exists(FILE_2):
    os.remove(FILE_2)

# open a file that isn't there, detect the error
try:
    book = open_book(FILE_1)
except GnucashException as backend_exception:
    print("OK", backend_exception)

# create a new file, this requires a file type specification
with create_book(FILE_2) as book:
    pass

# open the new file, try to open it a second time, detect the lock
# using the session as context manager automatically release the lock and close the session
with open_book(FILE_2) as book:
    try:
        with open_book(FILE_2) as book_2:
            pass
    except GnucashException as backend_exception:
        print("OK", backend_exception)

os.remove(FILE_2)

Creating an account

#!/usr/bin/env python
##  @file
#   @brief Example Script simple sqlite create
#   @ingroup python_bindings_examples

from __future__ import print_function
import os

from piecash import create_book, Account, Commodity, open_book
from piecash.core.factories import create_currency_from_ISO

filename = os.path.abspath("test.blob")
if os.path.exists(filename):
    os.remove(filename)

with create_book(filename) as book:
    a = Account(
        parent=book.root_account,
        name="wow",
        type="ASSET",
        commodity=create_currency_from_ISO("CAD"),
    )

    book.save()

with open_book(filename) as book:
    print(book.root_account.children)
    print(book.commodities.get(mnemonic="CAD"))

os.remove(filename)

Creating a transaction

#!/usr/bin/env python
# # @file
# @brief Creates a basic set of accounts and a couple of transactions
# @ingroup python_bindings_examples
from decimal import Decimal
import os
import tempfile

from piecash import create_book, Account, Transaction, Split, Commodity
from piecash.core.factories import create_currency_from_ISO

FILE_1 = os.path.join(tempfile.gettempdir(), "example.gnucash")

with create_book(FILE_1, overwrite=True) as book:
    root_acct = book.root_account
    cad = create_currency_from_ISO("CAD")
    expenses_acct = Account(
        parent=root_acct, name="Expenses", type="EXPENSE", commodity=cad
    )
    savings_acct = Account(parent=root_acct, name="Savings", type="BANK", commodity=cad)
    opening_acct = Account(
        parent=root_acct, name="Opening Balance", type="EQUITY", commodity=cad
    )
    num1 = Decimal("4")
    num2 = Decimal("100")
    num3 = Decimal("15")

    # create transaction with core objects in one step
    trans1 = Transaction(
        currency=cad,
        description="Groceries",
        splits=[
            Split(value=num1, account=expenses_acct),
            Split(value=-num1, account=savings_acct),
        ],
    )

    # create transaction with core object in multiple steps
    trans2 = Transaction(currency=cad, description="Opening Savings Balance")

    split3 = Split(value=num2, account=savings_acct, transaction=trans2)

    split4 = Split(value=-num2, account=opening_acct, transaction=trans2)

    # create transaction with factory function
    from piecash.core.factories import single_transaction

    trans3 = single_transaction(
        None, None, "Pharmacy", num3, savings_acct, expenses_acct
    )

    book.save()

Modifying existing transactions/splits

from piecash import open_book, ledger, Split

# open a book
with open_book(
    "../gnucash_books/simple_sample.gnucash", readonly=True, open_if_lock=True
) as mybook:
    # iterate on all the transactions in the book
    for transaction in mybook.transactions:
        # add some extra text to the transaction description
        transaction.description = (
            transaction.description + " (some extra info added to the description)"
        )
        # iterate over all the splits of the transaction
        # as we will modify the transaction splits in the loop,
        # we need to use list(...) to take a copy of the splits at the start of the loop
        for split in list(transaction.splits):
            # create the new split (here a copy of the each existing split
            # in the transaction with value/quantity divided by 10)
            new_split = Split(
                account=split.account,
                value=split.value / 10,
                quantity=split.quantity / 10,
                memo="my new split",
                transaction=transaction,  # attach the split to the current transaction
            )
    # register the changes (but not save)
    mybook.flush()

    # print the book in ledger format to view the changes
    print(ledger(mybook))

    # save the book
    # this will raise an error as readonly=True (change to readonly=False to successfully save the book)
    mybook.save()

Delete an account in a book

import csv
from pathlib import Path

from piecash import open_book, Account

GNUCASH_BOOK = "../gnucash_books/simple_sample.gnucash"

# open the book and the export file
with open_book(GNUCASH_BOOK, readonly=True, open_if_lock=True) as book:
    # show accounts
    print(book.accounts)
    print("Number of splits in the book:", len(book.splits))
    # select the 3rd account
    account = book.accounts[2]
    print(account, " has splits: ", account.splits)

    # delete the account from the book
    book.delete(account)
    # flush the change
    book.flush()
    # check the account has disappeared from the book and its related split too
    print(book.accounts)
    print("Number of splits in the book:", len(book.splits))

    # even if the account object and its related object still exists
    print(account, " has splits: ", account.splits)

    # do not forget to save the book if you want
    # your changes to be saved in the database

Save/cancel changes in a book

from __future__ import print_function
from piecash import create_book

# create by default an in memory sqlite version
with create_book(echo=False) as book:

    print("Book is saved:", book.is_saved, end=" ")
    print(" ==> book description:", book.root_account.description)

    print("changing description...")
    book.root_account.description = "hello, book"
    print("Book is saved:", book.is_saved, end=" ")
    print(" ==> book description:", book.root_account.description)

    print("saving...")
    book.save()

    print("Book is saved:", book.is_saved, end=" ")
    print(" ==> book description:", book.root_account.description)

    print("changing description...")
    book.root_account.description = "nevermind, book"
    print("Book is saved:", book.is_saved, end=" ")
    print(" ==> book description:", book.root_account.description)

    print("cancel...")
    book.cancel()

    print("Book is saved:", book.is_saved, end=" ")
    print(" ==> book description:", book.root_account.description)

Create a book with some accounts and add a transaction

from piecash import create_book, Account

# create a book with some account tree structure
with create_book(
    "../gnucash_books/simple_book_transaction_creation.gnucash", overwrite=True
) as mybook:
    mybook.root_account.children = [
        Account(
            name="Expenses",
            type="EXPENSE",
            commodity=mybook.currencies(mnemonic="USD"),
            placeholder=True,
            children=[
                Account(
                    name="Some Expense Account",
                    type="EXPENSE",
                    commodity=mybook.currencies(mnemonic="USD"),
                ),
            ],
        ),
        Account(
            name="Assets",
            type="ASSET",
            commodity=mybook.currencies(mnemonic="USD"),
            placeholder=True,
            children=[
                Account(
                    name="Current Assets",
                    type="BANK",
                    commodity=mybook.currencies(mnemonic="USD"),
                    placeholder=True,
                    children=[
                        Account(
                            name="Checking",
                            type="BANK",
                            commodity=mybook.currencies(mnemonic="USD"),
                        )
                    ],
                ),
            ],
        ),
    ]
    # save the book
    mybook.save()

from piecash import open_book, Transaction, Split
from datetime import datetime
from decimal import Decimal

# reopen the book and add a transaction
with open_book(
    "../gnucash_books/simple_book_transaction_creation.gnucash",
    open_if_lock=True,
    readonly=False,
) as mybook:
    today = datetime.now()
    # retrieve the currency from the book
    USD = mybook.currencies(mnemonic="USD")
    # define the amount as Decimal
    amount = Decimal("25.35")
    # retrieve accounts
    to_account = mybook.accounts(fullname="Expenses:Some Expense Account")
    from_account = mybook.accounts(fullname="Assets:Current Assets:Checking")
    # create the transaction with its two splits
    Transaction(
        post_date=today.date(),
        enter_date=today,
        currency=USD,
        description="Transaction Description!",
        splits=[
            Split(account=to_account, value=amount, memo="Split Memo!"),
            Split(account=from_account, value=-amount, memo="Other Split Memo!"),
        ],
    )
    # save the book
    mybook.save()

from piecash import ledger

# check the book by exporting to ledger format
with open_book(
    "../gnucash_books/simple_book_transaction_creation.gnucash", open_if_lock=True
) as mybook:
    print(ledger(mybook))

Export transactions to a CSV file

import csv
from pathlib import Path

from piecash import open_book

fields = [
    "DATE",
    "TRANSACTION VALUE",
    "DEBIT/CREDIT INDICATOR",
    "ACCOUNT",
    "ACCOUNT CODE",
    "CONTRA ACCOUNT",
    "CONTRA ACCOUNT CODE",
    "ENTRY TEXT",
]

GNUCASH_BOOK = "../gnucash_books/simple_sample.gnucash"
CSV_EXPORT = "export.csv"
REPORTING_YEAR = 2019

# open the book and the export file
with open_book(GNUCASH_BOOK, readonly=True, open_if_lock=True) as mybook, Path(
    CSV_EXPORT
).open("w", newline="") as f:
    # initialise the CSV writer
    csv_writer = csv.DictWriter(f, fieldnames=fields)
    csv_writer.writeheader()

    # iterate on all the transactions in the book
    for transaction in mybook.transactions:
        # filter transactions not in REPORTING_YEAR
        if transaction.post_date.year != REPORTING_YEAR:
            continue

        # handle only transactions with 2 splits
        if len(transaction.splits) != 2:
            print(
                f"skipping transaction {transaction} as it has more"
                f" than 2 splits in the transaction, dunno what to export to CSV"
            )
            continue

        # assign the two splits of the transaction
        split_one, split_two = transaction.splits
        # build the dictionary with the data of the transaction
        data = dict(
            zip(
                fields,
                [
                    transaction.post_date,
                    split_one.value,
                    split_one.is_debit,
                    split_one.account.name,
                    split_one.account.code,
                    split_two.account.name,
                    split_two.account.code,
                    transaction.description,
                ],
            )
        )
        # write the transaction to the CSV
        csv_writer.writerow(data)

Extract Split information as pandas DataFrame

from piecash import open_book

# open a book
with open_book("../gnucash_books/simple_sample.gnucash", open_if_lock=True) as mybook:
    # print all splits in account "Asset"
    asset = mybook.accounts(fullname="Asset")
    for split in asset.splits:
        print(split)

    # extract all split information to a pandas DataFrame
    df = mybook.splits_df()

    # print for account "Asset" some information on the splits
    print(df.loc[df["account.fullname"] == "Asset", ["transaction.post_date", "value"]])

Filtered transaction reports

from __future__ import print_function
import datetime
import re
import os.path

from piecash import open_book


if __name__ == "__main__":
    this_folder = os.path.dirname(os.path.realpath(__file__))
    s = open_book(
        os.path.join(this_folder, "..", "gnucash_books", "simple_sample.gnucash"),
        open_if_lock=True,
    )
else:
    s = open_book(
        os.path.join("gnucash_books", "simple_sample.gnucash"), open_if_lock=True
    )

# get default currency
print(s.default_currency)

regex_filter = re.compile("^/Rental/")

# retrieve relevant transactions
transactions = [
    tr
    for tr in s.transactions  # query all transactions in the book/session and filter them on
    if (
        regex_filter.search(tr.description)  # description field matching regex
        or any(regex_filter.search(spl.memo) for spl in tr.splits)
    )  # or memo field of any split of transaction
    and tr.post_date.date() >= datetime.date(2014, 11, 1)
]  # and with post_date no later than begin nov.


# output report with simple 'print'
print(
    "Here are the transactions for the search criteria '{}':".format(
        regex_filter.pattern
    )
)
for tr in transactions:
    print("- {:%Y/%m/%d} : {}".format(tr.post_date, tr.description))
    for spl in tr.splits:
        print(
            "\t{amount}  {direction}  {account} : {memo}".format(
                amount=abs(spl.value),
                direction="-->" if spl.value > 0 else "<--",
                account=spl.account.fullname,
                memo=spl.memo,
            )
        )

# same with jinja2 templates
try:
    import jinja2
except ImportError:
    print(
        "\n\t*** Install jinja2 ('pip install jinja2') to test the jinja2 template version ***\n"
    )
    jinja2 = None

if jinja2:
    env = jinja2.Environment(trim_blocks=True, lstrip_blocks=True)
    print(
        env.from_string(
            """
    Here are the transactions for the search criteria '{{regex.pattern}}':
    {% for tr in transactions %}
    - {{ tr.post_date.strftime("%Y/%m/%d") }} : {{ tr.description }}
      {% for spl in tr.splits %}
        {{ spl.value.__abs__() }} {% if spl.value < 0 %} --> {% else %} <-- {% endif %} {{ spl.account.fullname }} : {{ spl.memo }}
      {% endfor %}
    {% endfor %}
    """
        ).render(transactions=transactions, regex=regex_filter)
    )

piecash and the official python bindings

piecash is an alternative to the python bindings that may be bundled with gnucash (http://wiki.gnucash.org/wiki/Python_Bindings).

This page aims to give some elements of comparison between both python interfaces to better understand their relevancy to your needs. Information on the official python bindings may be incomplete (information gathered from mailing lists and wiki).

Gnucash 3.0.x series

piecash (>=1.0.0)

official python bindings (gnucash 3.0.n)

book format

gnucash 3.0.n

gnucash 3.0.n

environment

Python 3.6/3.7/3.8/3.9

Python 3

installation

pure python package ‘pip install piecash’

compilation (difficult on windows) binaries (available on Linux)

requires GnuCash

no

yes

runs on Android

yes

no

gnucash files

SQL backend only

SQL backend and XML

documentation

yes (read the docs) actively developed

partial

functionalities

creation of new books read/browse objects create objects (basic) update online prices

all functionalities provided by the GnuCash C/C++ engine

Gnucash 2.6.x series

piecash (<=0.18.0)

official python bindings (gnucash 2.6.n)

book format

gnucash 2.6.n

gnucash 2.6.n

environment

Python 2.7 & 3.3/3.4/3.5/3.6

Python 2.7

installation

pure python package ‘pip install piecash’

compilation (difficult on windows) binaries (available on Linux)

requires GnuCash

no

yes

runs on Android

yes

no

gnucash files

SQL backend only

SQL backend and XML

documentation

yes (read the docs) actively developed

partial

functionalities

creation of new books read/browse objects create objects (basic) update online prices

all functionalities provided by the GnuCash C/C++ engine

piecash on android

piecash can successfully run on android which opens interesting opportunities!

Installing termux

First, you have to install Termux from the Play Store.

You start Termux and:

  1. edit your .bash_profile with:

    export TZ=$(getprop persist.sys.timezone)
    export SHELL=$(which bash)
    
  2. add the folder ~/storage with access to your android folders (also accessible via USB sync):

    termux-setup-storage
    

Installing python and piecash

You start Termux on your android and then:

  1. Install python and pipenv:

    pkg install python
    pip install pipenv
    
  2. Install piecash for your project:

    mkdir my-project
    cd my-project
    pipenv install piecash
    
  3. Test piecash:

    pipenv shell
    python
    >>> import piecash
    

Use SSH with your android

You can ssh easily in your android thanks to Termux. For this, on Termux on your android:

  1. install openssh:

    pkg install openssh
    
  2. add your public key (id_rsa.pub) in the file .ssh/authorized_keys on Termux

  3. run the sshd server:

    sshd
    

On your machine (laptop, …):

  1. configure your machine to access your android device:

    Host android
       HostName 192.168.1.4  # <== put the IP address of your android
       User termux
       Port 8022
    
  2. log in your android from your machine:

    ssh android
    

Use the USB Debugging with your android

To be investigated…:

# on laptop
adb forward tcp:8022 tcp:8022 && ssh localhost -p 8022

# on android
# On Android 4.2 and higher, the Developer options screen is hidden by default. To make it visible, go to Settings > About phone and tap Build number seven times. Return to the previous screen to find Developer options at the bottom.
change USB Configuration to "charge only" or "PTP"

# downloading https://developer.android.com/studio/run/win-usb.html
# Click here to download the Google USB Driver ZIP file (Z
# install legacy hardware (in device manager)
# choose the folder of the zip drive and choose ADB interface

For developers

The complete api documentation (apidoc) :

piecash package

Subpackages

piecash.business package

Submodules
piecash.business.invoice module
piecash.business.person module
class piecash.business.person.Address(name='', addr1='', addr2='', addr3='', addr4='', email='', fax='', phone='')[source]

Bases: object

An Address object encapsulates information regarding an address in GnuCash.

name

self explanatory

Type

str

addr1

self explanatory

Type

str

addr2

self explanatory

Type

str

addr3

self explanatory

Type

str

addr4

self explanatory

Type

str

email

self explanatory

Type

str

fax

self explanatory

Type

str

phone

self explanatory

Type

str

class piecash.business.person.Person[source]

Bases: object

A mixin declaring common field for Customer, Vendor and Employee

class piecash.business.person.Customer(name, currency, id=None, notes='', active=1, tax_override=0, credit=Decimal('0'), discount=Decimal('0'), taxtable=None, address=None, shipping_address=None, tax_included='USEGLOBAL', book=None)[source]

Bases: piecash.business.person.Person, piecash._declbase.DeclarativeBaseGuid

A GnuCash Customer

name

name of the Customer

Type

str

id

autonumber id with 5 digits (initialised to book.counter_customer + 1)

Type

str

notes

notes

Type

str

active

1 if the customer is active, 0 otherwise

Type

int

discount

see Gnucash documentation

Type

decimal.Decimal

credit

see Gnucash documentation

Type

decimal.Decimal

currency

the currency of the customer

Type

piecash.core.commodity.Commodity

tax_override

1 if tax override, 0 otherwise

Type

int

address

the address of the customer

Type

Address

shipping_address

the shipping address of the customer

Type

Address

tax_included

‘yes’, ‘no’, ‘use global’

Type

str

taxtable

tax table of the customer

Type

piecash.business.tax.TaxTable

term

bill term of the customer

Type

piecash.business.invoice.Billterm

class piecash.business.person.Employee(name, currency, creditcard_account=None, id=None, active=1, acl='', language='', workday=Decimal('0'), rate=Decimal('0'), address=None, book=None)[source]

Bases: piecash.business.person.Person, piecash._declbase.DeclarativeBaseGuid

A GnuCash Employee

name

name of the Employee

Type

str

id

autonumber id with 5 digits (initialised to book.counter_employee + 1)

Type

str

language

language

Type

str

active

1 if the employee is active, 0 otherwise

Type

int

workday

see Gnucash documentation

Type

decimal.Decimal

rate

see Gnucash documentation

Type

decimal.Decimal

currency

the currency of the employee

Type

piecash.core.commodity.Commodity

address

the address of the employee

Type

Address

creditcard_account

credit card account for the employee

Type

piecash.core.account.Account

on_book_add()[source]

Call when the object is added to a book

class piecash.business.person.Vendor(name, currency, id=None, notes='', active=1, tax_override=0, taxtable=None, credit=Decimal('0'), discount=Decimal('0'), address=None, tax_included='USEGLOBAL', book=None)[source]

Bases: piecash.business.person.Person, piecash._declbase.DeclarativeBaseGuid

A GnuCash Vendor

name

name of the Vendor

Type

str

id

autonumber id with 5 digits (initialised to book.counter_vendor + 1)

Type

str

notes

notes

Type

str

active

1 if the vendor is active, 0 otherwise

Type

int

currency

the currency of the vendor

Type

piecash.core.commodity.Commodity

tax_override

1 if tax override, 0 otherwise

Type

int

address

the address of the vendor

Type

Address

tax_included

‘YES’, ‘NO’, ‘USEGLOBAL’

Type

str

taxtable

tax table of the vendor

Type

piecash.business.tax.TaxTable

term

bill term of the vendor

Type

piecash.business.invoice.Billterm

piecash.business.tax module
Module contents

piecash.core package

Submodules
piecash.core._commodity_helper module
piecash.core._commodity_helper.quandl_fx(fx_mnemonic, base_mnemonic, start_date)[source]

Retrieve exchange rate of commodity fx in function of base.

API KEY will be retrieved from the environment variable QUANDL_API_KEY

piecash.core.account module
class piecash.core.account.AccountType(value)[source]

Bases: enum.Enum

An enumeration.

class piecash.core.account.Account(name, type, commodity, parent=None, description='', commodity_scu=None, hidden=0, placeholder=0, code='', book=None, children=None)[source]

Bases: piecash._declbase.DeclarativeBaseGuid

A GnuCash Account which is specified by its name, type and commodity.

type

type of the Account

Type

str

sign

1 for accounts with positive balances, -1 for accounts with negative balances

Type

int

code

code of the Account

Type

str

commodity

the commodity of the account

Type

piecash.core.commodity.Commodity

commodity_scu

smallest currency unit for the account

Type

int

non_std_scu

1 if the scu of the account is NOT the same as the commodity

Type

int

description

description of the account

Type

str

name

name of the account

Type

str

fullname

full name of the account (including name of parent accounts separated by ‘:’)

Type

str

placeholder

1 if the account is a placeholder (should not be involved in transactions)

Type

int

hidden

1 if the account is hidden

Type

int

is_template

True if the account is a template account (ie commodity=template/template)

Type

bool

parent

the parent account of the account (None for the root account of a book)

Type

Account

children

the list of the children accounts

Type

list of Account

splits

the list of the splits linked to the account

Type

list of piecash.core.transaction.Split

lots

the list of lots to which the account is linked

Type

list of piecash.business.Lot

book

the book if the account is the root account (else None)

Type

piecash.core.book.Book

budget_amounts

list of budget amounts of the account

Type

list of piecash.budget.BudgetAmount

scheduled_transaction

scheduled transaction linked to the account

Type

piecash.core.transaction.ScheduledTransaction

object_to_validate(change)[source]

yield the objects to validate when the object is modified (change=”new” “deleted” or “dirty”).

For instance, if the object is a Split, if it changes, we want to revalidate not the split but its transaction and its lot (if any). split.object_to_validate should yeild both split.transaction and split.lot

validate()[source]

This must be reimplemented for object requiring validation

observe_commodity(key, value)[source]

Ensure update of commodity_scu when commodity is changed

get_balance(recurse=True, commodity=None, natural_sign=True, at_date=None)[source]

Returns the balance of the account (including its children accounts if recurse=True) expressed in account’s commodity/currency. If this is a stock/fund account, it will return the number of shares held. If this is a currency account, it will be in account’s currency. In case of recursion, the commodity of children accounts will be transformed to the commodity of the father account using the latest price (if no price is available to convert , it is considered as 0). If natural_sign is True, the sign of the balance is reverted for the account with type {‘LIABILITY’, ‘PAYABLE’, ‘CREDIT’, ‘INCOME’, ‘EQUITY’}

recurse

True if the balance should include children accounts (default to True)

Type

bool, optional

commodity

the currency into which to get the balance (default to None, i.e. the currency of the account)

Type

piecash.core.commodity.Commodity

natural_sign

True if the balance sign is reversed for accounts of type {‘LIABILITY’, ‘PAYABLE’, ‘CREDIT’, ‘INCOME’, ‘EQUITY’} (default to True)

Type

bool, optional

at_date

the sum() balance of the account at a given date based on transaction post date

Type

datetime.datetime

Returns

the balance of the account

piecash.core.book module
class piecash.core.book.Book(root_account=None, root_template=None)[source]

Bases: piecash._declbase.DeclarativeBaseGuid

A Book represents a GnuCash document. It is created through one of the two factory functions create_book() and open_book().

Canonical use is as a context manager like (the book is automatically closed at the end of the with block):

with create_book() as book:
    ...

Note

If you do not use the context manager, do not forget to close the session explicitly (book.close()) to release any lock on the file/DB.

The book puts at disposal several attributes to access the main objects of the GnuCash document:

# to get the book and the root_account
ra = book.root_account

# to get the list of accounts, commodities or transactions
for acc in book.accounts:  # or book.commodities or book.transactions
    # do something with acc

# to get a specific element of these lists
EUR = book.commodities(namespace="CURRENCY", mnemonic="EUR")

# to get a list of all objects of some class (even non core classes)
budgets = book.get(Budget)
# or a specific object
budget = book.get(Budget, name="my first budget")

You can check a session has changes (new, deleted, changed objects) by getting the book.is_saved property. To save or cancel changes, use book.save() or book.cancel():

# save a session if it is no saved (saving a unchanged session is a no-op)
if not book.is_saved:
    book.save()
root_account

the root account of the book

Type

piecash.core.account.Account

root_template

the root template of the book (usage not yet clear…)

Type

piecash.core.account.Account

default_currency

the currency of the root account (=default currency of the book)

Type

piecash.core.commodity.Commodity

uri

connection string of the book (set by the GncSession when accessing the book)

Type

str

session

the sqlalchemy session encapsulating the book

Type

sqlalchemy.orm.session.Session

use_trading_accounts

true if option “Use trading accounts” is enabled

Type

bool

use_split_action_field

true if option “Use Split Action Field for Number” is enabled

Type

bool

RO_threshold_day

value of Day Threshold for Read-Only Transactions (red line)

Type

int

control_mode

list of allowed non-standard operations like : “allow-root-subaccounts”

Type

list(str)

counter_customer

counter for piecash.business.person.Customer id (link to slot “counters/gncCustomer”)

Type

int

counter_vendor

counter for piecash.business.person.Vendor id (link to slot “counters/gncVendor”)

Type

int

counter_employee

counter for piecash.business.person.Employee id (link to slot “counters/gncEmployee”)

Type

int

counter_invoice

counter for piecash.business.invoice.Invoice id (link to slot “counters/gncInvoice”)

Type

int

counter_job

counter for piecash.business.invoice.Job id (link to slot “counters/gncJob”)

Type

int

counter_bill

counter for piecash.business.invoice.Bill id (link to slot “counters/gncBill”)

Type

int

counter_exp_voucher

counter for piecash.business.invoice.Invoice id (link to slot “counters/gncExpVoucher”)

Type

int

counter_order

counter for piecash.business.invoice.Order id (link to slot “counters/gncOrder”)

Type

int

business_company_phone

phone number of book company (link to slit “options/Business/Company Phone Number”)

Type

str

business_company_email

email of book company (link to slit “options/Business/Company Email Address”)

Type

str

business_company_contact

contact person of book company (link to slit “options/Business/Company Contact Person”)

Type

str

business_company_ID

ID of book company (link to slit “options/Business/Company ID”)

Type

str

business_company_name

name of book company (link to slit “options/Business/Company Name”)

Type

str

business_company_address

address of book company (link to slit “options/Business/Company Address”)

Type

str

business_company_website

website URL of book company (link to slit “options/Business/Company Website URL”)

Type

str

validate()[source]

This must be reimplemented for object requiring validation

static track_dirty(session, flush_context, instances)[source]

Record in session._all_changes the objects that have been modified before each flush

trading_account(cdty)[source]

Return the trading account related to the commodity. If it does not exist and the option “Use Trading Accounts” is enabled, create it on the fly

add(obj)[source]

Add an object to the book (to be used if object not linked in any way to the book)

delete(obj)[source]

Delete an object from the book (to remove permanently an object)

save()[source]

Save the changes to the file/DB (=commit transaction)

flush()[source]

Flush the book

cancel()[source]

Cancel all the changes that have not been saved (=rollback transaction)

property is_saved

Are all the changes saved to the file/DB?

You can check a session has changes (new, deleted, changed objects) by getting the book.is_saved property.

close()[source]

Close a session. Any changes not yet saved are rolled back. Any lock on the file/DB is released.

get(cls, **kwargs)[source]

Generic getter for a GnuCash object in the GncSession. If no kwargs is given, it returns the list of all objects of type cls (uses the sqlalchemy session.query(cls).all()). Otherwise, it gets the unique object which attributes match the kwargs (uses the sqlalchemy session.query(cls).filter_by(**kwargs).one() underneath):

# to get the first account with name="Income"
inc_account = session.get(Account, name="Income")

# to get all accounts
accs = session.get(Account)
Parameters
  • cls (class) – the class of the object to retrieve (Account, Price, Budget,…)

  • kwargs (dict) – the attributes to filter on

Returns

the unique object if it exists, raises exceptions otherwise

Return type

object

property transactions

gives easy access to all transactions in the book through a piecash.model_common.CallableList of piecash.core.transaction.Transaction

property splits

gives easy access to all splits in the book through a piecash.model_common.CallableList of piecash.core.transaction.Split

property accounts

gives easy access to all accounts in the book through a piecash.model_common.CallableList of piecash.core.account.Account

property commodities

gives easy access to all commodities in the book through a piecash.model_common.CallableList of piecash.core.commodity.Commodity

property invoices

gives easy access to all commodities in the book through a piecash.model_common.CallableList of piecash.core.commodity.Commodity

property currencies

gives easy access to all currencies in the book through a piecash.model_common.CallableList of piecash.core.commodity.Commodity

property prices

gives easy access to all prices in the book through a piecash.model_common.CallableList of piecash.core.commodity.Price

property customers

gives easy access to all commodities in the book through a piecash.model_common.CallableList of piecash.business.people.Customer

property vendors

gives easy access to all commodities in the book through a piecash.model_common.CallableList of piecash.business.people.Vendor

property employees

gives easy access to all commodities in the book through a piecash.model_common.CallableList of piecash.business.people.Employee

property taxtables

gives easy access to all commodities in the book through a piecash.model_common.CallableList of piecash.business.tax.Taxtable

property query

proxy for the query function of the underlying sqlalchemy session

splits_df(additional_fields=None)[source]

Return a pandas DataFrame with all splits (piecash.core.commodity.Split) from the book

Parameters

list

Returns

pandas.DataFrame

prices_df()[source]

Return a pandas DataFrame with all prices (piecash.core.commodity.Price) from the book

Returns

pandas.DataFrame

piecash.core.commodity module
exception piecash.core.commodity.GncCommodityError[source]

Bases: piecash._common.GnucashException

exception piecash.core.commodity.GncPriceError[source]

Bases: piecash._common.GnucashException

class piecash.core.commodity.Price(commodity, currency, date, value, type='unknown', source='user:price')[source]

Bases: piecash._declbase.DeclarativeBaseGuid

A single Price for a commodity.

commodity

commodity to which the Price relates

Type

Commodity

currency

currency in which the Price is expressed

Type

Commodity

date

date object representing the day at which the price is relevant

Type

datetime.date

source

source of the price

Type

str

type

last, ask, bid, unknown, nav

Type

str

value

the price itself

Type

decimal.Decimal

object_to_validate(change)[source]

yield the objects to validate when the object is modified (change=”new” “deleted” or “dirty”).

For instance, if the object is a Split, if it changes, we want to revalidate not the split but its transaction and its lot (if any). split.object_to_validate should yeild both split.transaction and split.lot

validate()[source]

This must be reimplemented for object requiring validation

class piecash.core.commodity.Commodity(namespace, mnemonic, fullname, fraction=100, cusip='', quote_flag=0, quote_source=None, quote_tz='', book=None)[source]

Bases: piecash._declbase.DeclarativeBaseGuid

A GnuCash Commodity.

cusip

cusip code

Type

str

fraction

minimal unit of the commodity (e.g. 100 for 1/100)

Type

int

namespace

CURRENCY for currencies, otherwise any string to group multiple commodities together

Type

str

mnemonic

the ISO symbol for a currency or the stock symbol for stocks (used for online quotes)

Type

str

quote_flag

1 if piecash/GnuCash quotes will retrieve online quotes for the commodity

Type

int

quote_source

the quote source for GnuCash (piecash always use yahoo for stock and quandl for currencies

Type

str

quote_tz

the timezone to assign on the online quotes

Type

str

base_currency

The base_currency for a commodity:

  • if the commodity is a currency, returns the “default currency” of the book (ie the one of the root_account)

  • if the commodity is not a currency, returns the currency encoded in the quoted_currency slot

Type

Commodity

accounts

list of accounts which have the commodity as commodity

Type

list of piecash.core.account.Account

transactions

list of transactions which have the commodity as currency

Type

list of piecash.core.transaction.Transaction

prices

iterator on prices related to the commodity (it is a sqlalchemy query underneath)

Type

iterator of Price

currency_conversion(currency)[source]

Return the latest conversion factor to convert self to currency

currency

the currency to which the Price need to be converted

Type

piecash.core.commodity.Commodity

Returns

a Decimal that can be multiplied by an amount expressed in self.commodity to get an amount expressed in currency

Raises

GncConversionError – not possible to convert self to the currency

update_prices(start_date=None)[source]

Retrieve online prices for the commodity:

  • for currencies, it will get from quandl the exchange rates between the currency and its base_currency

  • for stocks, it will get from yahoo the daily closing prices expressed in its base_currency

Parameters
  • start_date (datetime.date) – prices will be updated as of the start_date. If None, start_date is today

  • 7 days. (-) –

Note

if prices are already available in the GnuCash file, the function will only retrieve prices as of the max(start_date, last quoted price date)

Todo

add some frequency to retrieve prices only every X (week, month, …)

object_to_validate(change)[source]

yield the objects to validate when the object is modified (change=”new” “deleted” or “dirty”).

For instance, if the object is a Split, if it changes, we want to revalidate not the split but its transaction and its lot (if any). split.object_to_validate should yeild both split.transaction and split.lot

validate()[source]

This must be reimplemented for object requiring validation

piecash.core.currency_ISO module
class piecash.core.currency_ISO.ISO_type(country, currency, mnemonic, cusip, fraction)

Bases: tuple

property country

Alias for field number 0

property currency

Alias for field number 1

property cusip

Alias for field number 3

property fraction

Alias for field number 4

property mnemonic

Alias for field number 2

piecash.core.factories module
piecash.core.factories.create_stock_accounts(cdty, broker_account, income_account=None, income_account_types='D/CL/I')[source]

Create the multiple accounts used to track a single stock, ie:

  • broker_account/stock.mnemonic

and the following accounts depending on the income_account_types argument

  • D = Income/Dividend Income/stock.mnemonic

  • CL = Income/Cap Gain (Long)/stock.mnemonic

  • CS = Income/Cap Gain (Short)/stock.mnemonic

  • I = Income/Interest Income/stock.mnemonic

Parameters
  • broker_account (piecash.core.account.Account) – the broker account where the account holding

  • stock is to be created (the) –

  • income_account (piecash.core.account.Account) – the income account where the accounts holding

  • income related to the stock are to be created (the) –

  • income_account_types (str) – “/” separated codes to drive the creation of income accounts

Returns

a tuple with the account under the broker_account where the stock is held and the list of income accounts.

Return type

piecash.core.account.Account

piecash.core.factories.create_currency_from_ISO(isocode)[source]

Factory function to create a new currency from its ISO code

Parameters

isocode (str) – the ISO code of the currency (e.g. EUR for the euro)

Returns

the currency as a commodity object

Return type

Commodity

piecash.core.factories.create_stock_from_symbol(symbol, book=None)[source]

Factory function to create a new stock from its symbol. The ISO code of the quoted currency of the stock is stored in the slot “quoted_currency”.

Parameters

symbol (str) – the symbol for the stock (e.g. YHOO for the Yahoo! stock)

Returns

the stock as a commodity object

Return type

Commodity

Note

The information is gathered from the yahoo-finance package The default currency in which the quote is traded is stored in a slot ‘quoted_currency’

Todo

use ‘select * from yahoo.finance.sectors’ and ‘select * from yahoo.finance.industry where id =”sector_id”’ to retrieve name of stocks and allow therefore the creation of a stock by giving its “stock name” (or part of it). This could also be used to retrieve all symbols related to the same company

piecash.core.session module
class piecash.core.session.Version(table_name, table_version)[source]

Bases: sqlalchemy.ext.declarative.api.DeclarativeBase

The declarative class for the ‘versions’ table.

table_version

The version for the table

piecash.core.session.build_uri(sqlite_file=None, uri_conn=None, db_type=None, db_user=None, db_password=None, db_name=None, db_host=None, db_port=None, check_same_thread=True)[source]

Create the connection string in function of some choices.

Parameters
  • sqlite_file (str) – a path to an sqlite3 file (only used if uri_conn is None)

  • uri_conn (str) – a sqlalchemy connection string

  • db_type (str) – type of database in [“postgres”,”mysql”]

  • db_user (str) – username of database

  • db_password (str) – password for the use of database

  • db_name (str) – name of database

  • db_host (str) – host of database

  • db_port (int) – port of database

  • check_same_thread (bool) – sqlite flag that restricts connection use to the thread that created (see False for use in ipython/flask/… but read first https://docs.python.org/3/library/sqlite3.html)

Returns

the connection string

Return type

str

piecash.core.session.create_book(sqlite_file=None, uri_conn=None, currency='EUR', overwrite=False, keep_foreign_keys=False, db_type=None, db_user=None, db_password=None, db_name=None, db_host=None, db_port=None, check_same_thread=True, pg_template='template0', **kwargs)[source]

Create a new empty GnuCash book. If both sqlite_file and uri_conn are None, then an “in memory” sqlite book is created.

Parameters
  • sqlite_file (str) – a path to an sqlite3 file (only used if uri_conn is None)

  • uri_conn (str) – a sqlalchemy connection string

  • currency (str) – the ISO symbol of the default currency of the book

  • overwrite (bool) – True if book should be deleted and recreated if it exists already

  • keep_foreign_keys (bool) – True if the foreign keys should be kept (may not work at all with GnuCash)

  • db_type (str) – type of database in [“postgres”,”mysql”]

  • db_user (str) – username of database

  • db_password (str) – password for the use of database

  • db_name (str) – name of database

  • db_host (str) – host of database

  • db_port (int) – port of database

  • check_same_thread (bool) – sqlite flag that restricts connection use to the thread that created (see False for use in ipython/flask/… but read first https://docs.python.org/3/library/sqlite3.html)

  • pg_template (str) – the postgres template to use when creating the database. One of template1 or template0 (default template0). Irrelevant for other databases than postgres.

Returns

the document as a gnucash session

Return type

GncSession

Raises

GnucashException – if document already exists and overwrite is False

piecash.core.session.open_book(sqlite_file=None, uri_conn=None, readonly=True, open_if_lock=False, do_backup=True, db_type=None, db_user=None, db_password=None, db_name=None, db_host=None, db_port=None, check_same_thread=True, check_exists=True, **kwargs)[source]

Open an existing GnuCash book

Parameters
  • sqlite_file (str) – a path to an sqlite3 file (only used if uri_conn is None)

  • uri_conn (str) – a sqlalchemy connection string

  • readonly (bool) – open the file as readonly (useful to play with and avoid any unwanted save)

  • open_if_lock (bool) – open the file even if it is locked by another user (using open_if_lock=True with readonly=False is not recommended)

  • do_backup (bool) – do a backup if the file written in RW (i.e. readonly=False) (this only works with the sqlite backend and copy the file with .{:%Y%m%d%H%M%S}.gnucash appended to it)

  • db_type (str) – type of database in [“postgres”,”mysql”]

  • db_user (str) – username of database

  • db_password (str) – password for the use of database

  • db_name (str) – name of database

  • db_host (str) – host of database

  • db_port (str) – port of database

  • check_same_thread (bool) – sqlite flag that restricts connection use to the thread that created (see False for use in ipython/flask/… but read first https://docs.python.org/3/library/sqlite3.html)

  • check_exists (bool) – check if the database exists before connecting

Returns

the document as a gnucash session

Return type

GncSession

Raises
piecash.core.session.adapt_session(session, book, readonly)[source]

Change the SA session object to add some features.

Parameters
  • session – the SA session object that will be modified in place

  • book – the gnucash singleton book linked to the SA session

  • readonly – True if the session should not allow commits.

Returns

piecash.core.transaction module
class piecash.core.transaction.Split(account, value, quantity=None, transaction=None, memo='', action='', reconcile_date=None, reconcile_state='n', lot=None)[source]

Bases: piecash._declbase.DeclarativeBaseGuid

A GnuCash Split.

Note

A split used in a scheduled transaction has its main attributes in form of slots.

transaction

transaction of the split

Type

piecash.core.transaction.Transaction

account

account of the split

Type

piecash.core.account.Account

lot

lot to which the split pertains

Type

piecash.business.Lot

memo

memo of the split

Type

str

value

amount express in the currency of the transaction of the split

Type

decimal.Decimal

quantity

amount express in the commodity of the account of the split

Type

decimal.Decimal

reconcile_state

‘n’, ‘c’ or ‘y’

Type

str

reconcile_date

time

Type

datetime.datetime

action

describe the type of action behind the split (free form string but with dropdown in the GUI

Type

str

object_to_validate(change)[source]

yield the objects to validate when the object is modified (change=”new” “deleted” or “dirty”).

For instance, if the object is a Split, if it changes, we want to revalidate not the split but its transaction and its lot (if any). split.object_to_validate should yeild both split.transaction and split.lot

validate()[source]

This must be reimplemented for object requiring validation

class piecash.core.transaction.Transaction(currency, description='', notes=None, splits=None, enter_date=None, post_date=None, num='')[source]

Bases: piecash._declbase.DeclarativeBaseGuid

A GnuCash Transaction.

currency

currency of the transaction. This attribute is write-once (i.e. one cannot change it after being set)

Type

piecash.core.commodity.Commodity

description

description of the transaction

Type

str

enter_date

datetimetime at which transaction is entered

Type

datetime.datetime

post_date

day on which transaction is posted

Type

datetime.date

num

user provided transaction number

Type

str

splits

list of the splits of the transaction

Type

list of Split

scheduled_transaction

scheduled transaction behind the transaction

Type

ScheduledTransaction

notes

notes on the transaction (provided via a slot)

Type

str

object_to_validate(change)[source]

yield the objects to validate when the object is modified (change=”new” “deleted” or “dirty”).

For instance, if the object is a Split, if it changes, we want to revalidate not the split but its transaction and its lot (if any). split.object_to_validate should yeild both split.transaction and split.lot

validate()[source]

This must be reimplemented for object requiring validation

calculate_imbalances()[source]

Calculate value and quantity imbalances of a transaction

class piecash.core.transaction.ScheduledTransaction(*args, **kwargs)[source]

Bases: piecash._declbase.DeclarativeBaseGuid

A GnuCash Scheduled Transaction.

Attributes

adv_creation (int) : days to create in advance (0 if disabled) adv_notify (int) : days to notify in advance (0 if disabled) auto_create (bool) : auto_notify (bool) : enabled (bool) : start_date (datetime.datetime) : date to start the scheduled transaction last_occur (datetime.datetime) : date of last occurence of the schedule transaction end_date (datetime.datetime) : date to end the scheduled transaction (num/rem_occur should be 0) instance_count (int) : name (str) : name of the scheduled transaction num_occur (int) : number of occurences in total (end_date should be null) rem_occur (int) : number of remaining occurences (end_date should be null) template_account (piecash.core.account.Account): template account of the transaction

class piecash.core.transaction.Lot(title, account, notes='', splits=None, is_closed=0)[source]

Bases: piecash._declbase.DeclarativeBaseGuid

A GnuCash Lot. Each lot is linked to an account. Splits in this account can be associated to a Lot. Whenever the balance of the splits goes to 0, the Lot is closed (otherwise it is opened)

is_closed

1 if lot is closed, 0 otherwise

Type

int

account

account of the Lot

Type

piecash.core.account.Account

splits

splits associated to the Lot

Type

piecash.core.transaction.Split

object_to_validate(change)[source]

yield the objects to validate when the object is modified (change=”new” “deleted” or “dirty”).

For instance, if the object is a Split, if it changes, we want to revalidate not the split but its transaction and its lot (if any). split.object_to_validate should yeild both split.transaction and split.lot

validate()[source]

This must be reimplemented for object requiring validation

Module contents

Submodules

piecash._common module

exception piecash._common.GnucashException[source]

Bases: Exception

exception piecash._common.GncNoActiveSession[source]

Bases: piecash._common.GnucashException

exception piecash._common.GncValidationError[source]

Bases: piecash._common.GnucashException

exception piecash._common.GncImbalanceError[source]

Bases: piecash._common.GncValidationError

exception piecash._common.GncConversionError[source]

Bases: piecash._common.GnucashException

class piecash._common.Recurrence(*args, **kwargs)[source]

Bases: sqlalchemy.ext.declarative.api.DeclarativeBase

Recurrence information for scheduled transactions

obj_guid

link to the parent ScheduledTransaction record.

Type

str

recurrence_mult

Multiplier for the period type. Describes how many times the period repeats for the next occurrence.

Type

int

recurrence_period_type

type or recurrence (monthly, daily).

Type

str

recurrence_period_start

the date the recurrence starts.

Type

date

recurrence_weekend_adjust

adjustment to be made if the next occurrence falls on weekend / non-working day.

Type

str

piecash._common.hybrid_property_gncnumeric(num_col, denom_col)[source]

Return an hybrid_property handling a Decimal represented by a numerator and a denominator column. It assumes the python field related to the sqlcolumn is named as _sqlcolumn.

Returns

sqlalchemy.ext.hybrid.hybrid_property

class piecash._common.CallableList(*args)[source]

Bases: list

A simple class (inherited from list) allowing to retrieve a given list element with a filter on an attribute.

It can be used as the collection_class of a sqlalchemy relationship or to wrap any list (see examples in piecash.core.session.GncSession)

get(**kwargs)

Return the first element of the list that has attributes matching the kwargs dict. The get method is an alias for this method.

To be used as:

l(mnemonic="EUR", namespace="CURRENCY")
piecash._common.get_system_currency_mnemonic()[source]

Returns the mnemonic of the locale currency (and EUR if not defined).

At the target, it could also look in Gnucash configuration/registry to see if the user has chosen another default currency.

piecash._declbase module

piecash.budget module

class piecash.budget.Budget(*args, **kwargs)[source]

Bases: piecash._declbase.DeclarativeBaseGuid

A GnuCash Budget

name

name of the budget

Type

str

description

description of the budget

Type

str

amounts

list of amounts per account

Type

list of piecash.budget.BudgetAmount

class piecash.budget.BudgetAmount(*args, **kwargs)[source]

Bases: sqlalchemy.ext.declarative.api.DeclarativeBase

A GnuCash BudgetAmount

amount

the budgeted amount

Type

decimal.Decimal

account

the budgeted account

Type

piecash.core.account.Account

budget

the budget of the amount

Type

Budget

piecash.kvp module

class piecash.kvp.KVP_Type(value)[source]

Bases: enum.Enum

An enumeration.

class piecash.kvp.SlotType(*args, **kwargs)[source]

Bases: sqlalchemy.sql.type_api.TypeDecorator

Used to customise the DateTime type for sqlite (ie without the separators as in gnucash

impl

alias of sqlalchemy.sql.sqltypes.INTEGER

process_bind_param(value, dialect)[source]

Receive a bound parameter value to be converted.

Subclasses override this method to return the value that should be passed along to the underlying TypeEngine object, and from there to the DBAPI execute() method.

The operation could be anything desired to perform custom behavior, such as transforming or serializing data. This could also be used as a hook for validating logic.

This operation should be designed with the reverse operation in mind, which would be the process_result_value method of this class.

Parameters
  • value – Data to operate upon, of any type expected by this method in the subclass. Can be None.

  • dialect – the Dialect in use.

process_result_value(value, dialect)[source]

Receive a result-row column value to be converted.

Subclasses should implement this method to operate on data fetched from the database.

Subclasses override this method to return the value that should be passed back to the application, given a value that is already processed by the underlying TypeEngine object, originally from the DBAPI cursor method fetchone() or similar.

The operation could be anything desired to perform custom behavior, such as transforming or serializing data. This could also be used as a hook for validating logic.

Parameters
  • value – Data to operate upon, of any type expected by this method in the subclass. Can be None.

  • dialect – the Dialect in use.

This operation should be designed to be reversible by the “process_bind_param” method of this class.

piecash.ledger module

piecash.metadata module

Project metadata

Information describing the project.

piecash.sa_extra module

piecash.sa_extra.compile_datetime(element, compiler, **kw)[source]

data type for the date field

note: it went from TEXT(14) in 2.6 to TEXT(19) in 2.8 to accommodate for the new ISO format of date in sqlite

piecash.sa_extra.mapped_to_slot_property(col, slot_name, slot_transform=<function <lambda>>)[source]

Assume the attribute in the class as the same name as the table column with “_” prepended

piecash.sa_extra.pure_slot_property(slot_name, slot_transform=<function <lambda>>, ignore_invalid_slot=False)[source]

Create a property (class must have slots) that maps to a slot

Parameters
  • slot_name – name of the slot

  • slot_transform – transformation to operate before assigning value

  • ignore_invalid_slot – True if incorrect values (usually due to deleted data) should be converted to None

Returns

piecash.sa_extra.get_foreign_keys(metadata, engine)[source]

Retrieve all foreign keys from metadata bound to an engine :param metadata: :param engine: :return:

class piecash.sa_extra.ChoiceType(choices, **kw)[source]

Bases: sqlalchemy.sql.type_api.TypeDecorator

process_bind_param(value, dialect)[source]

Receive a bound parameter value to be converted.

Subclasses override this method to return the value that should be passed along to the underlying TypeEngine object, and from there to the DBAPI execute() method.

The operation could be anything desired to perform custom behavior, such as transforming or serializing data. This could also be used as a hook for validating logic.

This operation should be designed with the reverse operation in mind, which would be the process_result_value method of this class.

Parameters
  • value – Data to operate upon, of any type expected by this method in the subclass. Can be None.

  • dialect – the Dialect in use.

process_result_value(value, dialect)[source]

Receive a result-row column value to be converted.

Subclasses should implement this method to operate on data fetched from the database.

Subclasses override this method to return the value that should be passed back to the application, given a value that is already processed by the underlying TypeEngine object, originally from the DBAPI cursor method fetchone() or similar.

The operation could be anything desired to perform custom behavior, such as transforming or serializing data. This could also be used as a hook for validating logic.

Parameters
  • value – Data to operate upon, of any type expected by this method in the subclass. Can be None.

  • dialect – the Dialect in use.

This operation should be designed to be reversible by the “process_bind_param” method of this class.

Module contents

Python interface to GnuCash documents

An overall view on the core objects in GnuCash:

GnuCash SQL Object model and schema

A clear documentation of the SQL schema (tables, columns, relationships) and the implicit semantic (invariants that should be always satisfied, logic to apply in ambiguous/corner cases) is critical for piecash to

  1. ensure data integrity (when creating new objects and/or modifying/deleting existing objects)

  2. ensure compatibility in semantic with the official GnuCash application

Warning

This document explains what the author understands in these domains. It is not the reference documentation, please refer to the official GnuCash documentation for this.

Warning

Disclaimer : piecash primary focus is on reading GnuCash books and creating new Core objects. Creating other objects than the core objects, modifying existing objects attributes or relationships and deleting objects can be done through piecash but at the user’s own risk (backup your books before doing any of such modifications)

Schema

The following SQL schema has been generated by sadisplay (https://pypi.python.org/pypi/sadisplay) on a GnuCash book generated by piecash on the MySQL backend with the option keep_foreign_keys (the official GnuCash schema does not define foreign keys):

_images/schema.png

Days, times, dates & datetimes

The use of date and time in GnuCash is somewhat complicated (mainly due to legacy reasons). This chapter described how days and time are encoded in the different tables. For each table.field, the DB type and the PIECASH type are given (DATE = day, DATETIME = day + time) as well as the representation in SQL. All examples are based on a local time in CET (central european time) and for the 11 feb 2018.

prices.date
DATETIME -> DAY = YYYY-MM-DD 00:00:00 LT expressed as UTC (e.g. 20180210230000) if price entered via the price editor
DATETIME -> DAY = YYYY-MM-DD 10:59:00 UTC (e.g. 20180211105900) if price generated via a transaction
DATETIME -> DAY = ??? (To be completed)) if price retrieved via Finance:Quote
transactions.post_date

DATETIME -> DAY = YYYY-MM-DD 10:59:00 UTC (e.g. 20180211105900) In the slots, the date-posted stores the post_date as a day (e.g. 20180211)

transactions.enter_date

DATETIME -> DATETIME = YYYY-MM-DD hh:mm:ss UTC (e.g. 20180211123036)

splits.reconcile_date

DATETIME -> DAY = 1970-01-01 00:00:00 UTC if not applicable (19700101000000) DATETIME -> DAY = YYYY-MM-DD 23:59:59 LT expressed as UTC (e.g. 20180211225959) In the slots, the reconcile-info/last-date stored and int64 representing the timestamp of the last post_date (e.g. 1518389999 ~ 20180211105900)

schedxactions.start_date, schedxactions.end_date, schedxactions.last_occur

DAY -> DAY = YYYY-MM-DD

Core objects

There are 5 core objects in GnuCash : Book, Commodity, Account, Transaction, Split. An additional object, the Price, is strongly linked to the Commodity and is used in reports and for display (for instance, to convert all accounts balance in the default currency). While not as core as the others, it is an essential piece of functionality for anyone using GnuCash to track a stock portfolio value or multi-currency book.

Note

A priori, all these objects are all “create once, never change” objects. Changing some fields of an object may lead to complex renormalisation procedures. Deleting some objects may lead to complex cascade changes/renormalisation procedures. In this respect, it is important to either avoid changes/deletions or to have clear invariants that should stay true at any time.

Book

The Book is the object model representing a GnuCash document. It has a link to the root account, the account at the root of the tree structure.

Fields
root_account (mandatory)

The account at the root of the tree structure

root_template (mandatory)

Use to attach split from template/scheduled transactions

Invariant
  • one (and only one) Book per GnuCash document

Commodity

A Commodity is either a currency (€, $, …) or a commodity/stock that can be stored in/traded through an Account.

The Commodity object is used in two different (but related) contexts.

  1. each Account should specify the Commodity it handles/stores. For usual accounts (Savings, Expenses, etc), the Commodity is a currency. For trading accounts, the Commodity is usually a stock (AMZN, etc). In this role, each commodity (be it a stock or a currency) can have Prices attached to it that give the value of the commodity expressed in a given currency.

  2. each Transaction should specify the Currency which is used to balance itself.

Fields
namespace (mandatory)

A string representing the group/class of the commodity. All commodities that are currencies should have ‘CURRENCY’ as namespace. Non currency commodities should have other groups.

mnemonic (mandatory)

The symbol/stock sticker of the commodity (relevant for online download of quotes)

fullname

The full name for the commodity. Besides the fullname, there is a “calculated property” unique_name equal to “namespace::mnemonic”

cusip

unique code for the commodity

fraction

The smallest unit that can be accounted for (for a currency, this is equivalent to the scu, the smallest currency unit) This is essentially used for a) display and b) roundings

quote_flag

True if Prices for the commodity should be retrieved for the given stock. This is used by the “quote download” functionnality.

quote_source

The source for online download of quotes

Invariant
  • a currency commodity has namespace==’CURRENCY’

  • only currencies referenced by accounts or commodities are stored in the table ‘commodities’ (the complete list of currencies is available within the GnuCash application)

  • a stock commodity has namespace!=’CURRENCY’

Account

An account tracks some commodity for some business purpose. Changes in the commodity amounts are modelled through Splits (see Transaction & Splits).

Fields
type (mandatory)

the type of the account as string

commodity (mandatory)

The commodity that is handled by the account

parent (almost mandatory)

the parent account to which the account is attached. All accounts but the root_account should have a parent account.

commodity_scu (mandatory)

The smallest currency/commodity unit is similar to the fraction of a commodity. It is the smallest amount of the commodity that is tracked in the account. If it is different than the fraction of the commodity to which the account is linked, the field non_std_scu is set to 1 (otherwise the latter is set to 0).

name

self-explanatory

description

self-explanatory

placeholder

if True/1, the account cannot be involved in transactions through splits (ie it can only be the parent of other accounts). if False/0, the account can have Splits referring to it (as well as be the parent of other accounts). This field, if True, is also stored as a Slot under the key “placeholder” as a string “true”.

hidden

if True/1, the account will not be displayed in the GnuCash GUI Accounts tab and can be easily excluded from GnuCash GUI Reports. if False/0, the account will be displayed in the GnuCash GUI Accounts tab.

Invariant
  • if placeholder, no new splits can be created/changed (like a “freeze”)

  • only two accounts can have type ROOT (the root_account and the root_template of the book).

  • the type of an account is constrained by the type of the parent account

  • trading account are used when the option “use trading accounts” is enabled

Transaction & Splits

The transaction represents movement of money between accounts expressed in a given currency (the currency of the transaction). The transaction is modelled through a set of Splits (2 or more). Each Split is linked to an Account and gives the increase/decrease in units of the account commodity (quantity) related to the transaction as well as the equivalent amount in currency (value). For a given transaction, the sum of the split expressed in the currency (value) should be balanced.

Fields for Transaction
currency (mandatory)

The currency of the transaction

num (optional)

A transaction number (only used for information)

post_date (mandatory)

self-explanatory. This field is also stored as a slot under the date-posted key (as a date instead of a time)

enter_date (mandatory)

self-explanatory

description (mandatory)

self-explanatory

Fields for Split
tx (mandatory)

the transaction of the split

account (mandatory)

the account to which the split refers to

value (mandatory)

the value of the split expressed in the currency of the transaction

quantity (mandatory)

the change in quantity of the account expressed in the commodity of the account

reconcile information

(Descriptions from official help manual.)

  • n - Default status when a transaction is created

  • c - Cleared. Status may be assigned either manually or by an import process.

  • y - Status assigned solely by the reconciliation process. Places limits optionally requiring confirmation on editing fields in that line of a transaction.

  • f - Frozen. Not implemented at this time

  • v - Voided. Status is assigned or released manually and applies to every line in the transaction. It hides most of the transaction details but does not delete them. When a transaction is voided a reason entry is required that appears to the right of the description. (Note: There appears to be no way to actually view the reason in the GnuCash GUI at the moment.)

lot

reference to the lot (to be investigated)

Invariant
  • the sum of the value on all splits in a transaction should = 0 (transaction is balanced). If it is not the case, the GnuCash application create automatically an extra Split entry towards the Account Imbalance-XXX (with XXX the currency of the transaction)

  • the value and quantity fields are expressed as numerator / denominator. The denominator of the value should be the same as the fraction of the currency. The denominator of the quantity should be the same as the commodity_scu of the account.

  • the currency of a transaction is the currency of the account into which it is created in the GUI

  • if “use trading accounts” is enabled then the sum of quantities per commodity should also be balanced. This is done thanks to the automatic creation of splits with trading accounts (of type TRADING)

  • the reconcile field in all splits in a transaction that is voided are set to v

  • a voided transaction has 4 associated slots with obj_guid equal to the transaction’s guid and slot_type 4:

    • name: notes, string_val: Voided transaction

    • name: trans-read-only, string_val: Transaction Voided

    • name: void-reason, string_val: <user-supplied reason string>

    • name: void-time, string_val: date as string in format YYYY-MM-DD HH:mm:ss.nnnnnn pZZZZ where n represents milliseconds, p is an optionally present minus sign, and ZZZZ is GMT offset in HHmm format.

  • a voided split has 2 nearly identical associated slots with obj_guid equal to the split’s guid and slot_type 3:

    • name: void-former-amount, numeric_val_num/numeric_val_denom: the value of the voided split

    • name: void-former-value, numeric_val_num/numeric_val_denom: the value of the voided split

Price

The Price represent the value of a commodity in a given currency at some time.

It is used for exchange rates and stock valuation.

Fields
commodity (mandatory)

the commodity related to the Price

currency (mandatory)

The currency of the Price

date (mandatory)

self-explanatory (expressed in UTC)

value (mandatory)

the value in currency of the commodity

Invariant
  • the value is expressed as numerator / denominator. The denominator of the value should be the same as the fraction of the currency.

A list of resources used for the project:

Resources

This page lists resources related to GnuCash, and more specifically, to the use of Python for GnuCash.

Web resources

Blogs & discussions

Thanks

None of this could be possible without :

  • the GnuCash project, its core team of developers and its active community of users

  • python and its packages amongst which sqlalchemy

  • github, readthedocs and travis-ci for managing code, docs and testing

The todo list:

Indices and tables