r19314 - gnucash/trunk/src/optional/python-bindings - Nice example python scripts, patch by Mark Jenkins.

Geert Janssens gjanssens at code.gnucash.org
Wed Jun 30 15:26:57 EDT 2010


Author: gjanssens
Date: 2010-06-30 15:26:57 -0400 (Wed, 30 Jun 2010)
New Revision: 19314
Trac: http://svn.gnucash.org/trac/changeset/19314

Added:
   gnucash/trunk/src/optional/python-bindings/example_scripts/account_analysis.py
   gnucash/trunk/src/optional/python-bindings/example_scripts/new_book_with_opening_balances.py
   gnucash/trunk/src/optional/python-bindings/example_scripts/simple_sqlite_create.py
Modified:
   gnucash/trunk/src/optional/python-bindings/Makefile.am
Log:
Nice example python scripts, patch by Mark Jenkins.

Modified: gnucash/trunk/src/optional/python-bindings/Makefile.am
===================================================================
--- gnucash/trunk/src/optional/python-bindings/Makefile.am	2010-06-30 19:22:52 UTC (rev 19313)
+++ gnucash/trunk/src/optional/python-bindings/Makefile.am	2010-06-30 19:26:57 UTC (rev 19314)
@@ -55,6 +55,10 @@
   example_scripts/simple_test.py \
   example_scripts/simple_business_create.py \
   example_scripts/simple_invoice_insert.py \
+  example_scripts/simple_sqlite_create.py \
+  example_scripts/change_tax_code.py \
+  example_scripts/account_analysis.py \
+  example_scripts/new_book_with_opening_balances.py \
   glib.i
 
 MAINTAINERCLEANFILES = gnucash-core.c

Added: gnucash/trunk/src/optional/python-bindings/example_scripts/account_analysis.py
===================================================================
--- gnucash/trunk/src/optional/python-bindings/example_scripts/account_analysis.py	                        (rev 0)
+++ gnucash/trunk/src/optional/python-bindings/example_scripts/account_analysis.py	2010-06-30 19:26:57 UTC (rev 19314)
@@ -0,0 +1,258 @@
+#!/usr/bin/env python
+
+# account_analysis.py -- Output all the credits and debits on an account
+#
+# Copyright (C) 2009, 2010 ParIT Worker Co-operative <transparency at parit.ca>
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, contact:
+# Free Software Foundation           Voice:  +1-617-542-5942
+# 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+# Boston, MA  02110-1301,  USA       gnu at gnu.org
+#
+# @author Mark Jenkins, ParIT Worker Co-operative <mark at parit.ca>
+
+
+# python imports
+from sys import argv, stdout
+from datetime import date, timedelta
+from bisect import bisect_right
+from decimal import Decimal
+from math import log10
+import csv
+
+# gnucash imports
+from gnucash import Session, GncNumeric, Split
+
+# Invoke this script like the following example
+# $ gnucash-env python account_analysis.py gnucash_file.gnucash \
+#   2010 1 monthly 12 \
+#   debits-show credits-show Assets 'Test Account'
+#
+# That will do an analysis on the account 'Assets:Test Account' from
+# gnucash_file.xac, all of the debits and all of the credits will be shown
+# and summed on for 12 monthly periods starting from January (1st month) 2010
+# 
+# if you just want to see the credit and debit sums for each period, use
+# the debits-noshow and credits-noshow argument
+#
+# The output goes to stdout and is in csv format.
+#
+# Account path arugments are space separated, so you need to quote parts of
+# the account path with spaces in them
+#
+# available period types are monthly quarterly and yearly
+#
+# At the moment this script only supports GnuCash files of the sqllite3 type
+# its an easy edit to switch to xml: etc...
+
+
+# a dictionary with a period name as key, and number of months in that
+# kind of period as the value
+PERIODS = {"monthly": 1,
+           "quarterly": 3,
+           "yearly": 12 }
+
+NUM_MONTHS = 12
+
+ONE_DAY = timedelta(days=1)
+
+DEBITS_SHOW, CREDITS_SHOW = ("debits-show", "credits-show")
+
+ZERO = Decimal(0)
+
+def gnc_numeric_to_python_Decimal(numeric):
+    negative = numeric.negative_p()
+    if negative:
+        sign = 1
+    else:
+        sign = 0
+    copy = GncNumeric(numeric.num(), numeric.denom())
+    result = copy.to_decimal(None)
+    if not result:
+        raise Exception("gnc numeric value %s can't be converted to deciaml" %
+                        copy.to_string() )
+    digit_tuple = tuple( int(char)
+                         for char in str(copy.num())
+                         if char != '-' )
+    denominator = copy.denom()
+    exponent = int(log10(denominator))
+    assert( (10 ** exponent) == denominator )
+    return Decimal( (sign, digit_tuple, -exponent) )
+    
+
+def next_period_start(start_year, start_month, period_type):
+    # add numbers of months for the period length
+    end_month = start_month + PERIODS[period_type]
+    # use integer division to find out if the new end month is in a different
+    # year, what year it is, and what the end month number should be changed
+    # to.
+    # Because this depends on modular arithmatic, we have to curvert the month
+    # values from 1-12 to 0-11 by subtracting 1 and putting it back after
+    #
+    # the really cool part is that this whole thing is implemented without
+    # any branching; if end_month > NUM_MONTHS
+    #
+    # A the super nice thing is that you can add all kinds of period lengths
+    # to PERIODS
+    end_year = start_year + ( (end_month-1) / NUM_MONTHS )
+    end_month = ( (end_month-1) % NUM_MONTHS ) + 1
+
+    return end_year, end_month
+    
+
+def period_end(start_year, start_month, period_type):
+    if period_type not in PERIODS:
+        raise Exception("%s is not a valid period, should be %s" % (
+                period_type, str(PERIODS.keys()) ) )
+
+    end_year, end_month = next_period_start(start_year, start_month,
+                                            period_type)
+
+    # last step, the end date is day back from the start of the next period
+    # so we get a period end like
+    # 2010-03-31 for period starting 2010-01 instead of 2010-04-01
+    return date(end_year, end_month, 1) - ONE_DAY
+    
+
+def generate_period_boundaries(start_year, start_month, period_type, periods):
+    for i in xrange(periods):
+        yield ( date(start_year, start_month, 1),
+                period_end(start_year, start_month, period_type) )
+        start_year, start_month = next_period_start(start_year, start_month,
+                                                    period_type)
+
+def account_from_path(top_account, account_path, original_path=None):
+    if original_path==None: original_path = account_path
+    account, account_path = account_path[0], account_path[1:]
+    account = top_account.lookup_by_name(account)
+    if account.get_instance() == None:
+        raise Exception(
+            "path " + ''.join(original_path) + " could not be found")
+    if len(account_path) > 0 :
+        return account_from_path(account, account_path, original_path)
+    else:
+        return account
+
+
+def main():
+    (gnucash_file, start_year, start_month, period_type, periods,
+     debits_show, credits_show) = argv[1:8]
+    start_year, start_month, periods = [int(blah)
+                                        for blah in (start_year, start_month,
+                                                     periods) ]
+
+    debits_show = debits_show == DEBITS_SHOW
+    credits_show = credits_show == CREDITS_SHOW
+
+    account_path = argv[8:]
+
+    gnucash_session = Session("sqlite3://%s" % gnucash_file, is_new=False)
+    root_account = gnucash_session.book.get_root_account()
+    account_of_interest = account_from_path(root_account, account_path)
+
+    # a list of all the periods of interest, for each period
+    # keep the start date, end date, a list to store debits and credits,
+    # and sums for tracking the sum of all debits and sum of all credits
+    period_list = [
+        [start_date, end_date,
+         [], # debits
+         [], # credits
+         ZERO, # debits sum
+         ZERO, # credits sum
+         ]
+        for start_date, end_date in generate_period_boundaries(
+            start_year, start_month, period_type, periods)
+        ]
+    # a copy of the above list with just the period start dates
+    period_starts = [e[0] for e in period_list ]
+    
+    # insert and add all splits in the periods of interest
+    for split in account_of_interest.GetSplitList():
+        split = Split(instance=split)
+        trans = split.parent
+        trans_date = date.fromtimestamp(trans.GetDate())
+
+        # use binary search to find the period that starts before or on
+        # the transaction date
+        period_index = bisect_right( period_starts, trans_date ) - 1
+        
+        # ignore transactions with a date before the matching period start
+        # (after subtracting 1 above start_index would be -1)
+        # and after the last period_end
+        if period_index >= 0 and \
+                trans_date <= period_list[len(period_list)-1][1]:
+
+            # get the period bucket appropriate for the split in question
+            period = period_list[period_index]
+
+            # more specifically, we'd expect the transaction date
+            # to be on or after the period start, and  before or on the
+            # period end, assuming the binary search (bisect_right)
+            # assumptions from above are are right..
+            #
+            # in other words, we assert our use of binary search
+            # and the filtered results from the above if provide all the
+            # protection we need
+            assert( trans_date>= period[0] and trans_date <= period[1] )
+            
+            split_amount = gnc_numeric_to_python_Decimal(split.GetAmount())
+
+            # if the amount is negative, this is a credit
+            if split_amount < ZERO:
+                debit_credit_offset = 1
+            # else a debit
+            else:
+                debit_credit_offset = 0
+
+            # store the debit or credit Split with its transaction, using the
+            # above offset to get in the right bucket
+            #
+            # if we wanted to be really cool we'd keep the transactions
+            period[2+debit_credit_offset].append( (trans, split) )
+    
+            # add the debit or credit to the sum, using the above offset
+            # to get in the right bucket
+            period[4+debit_credit_offset] += split_amount
+
+    csv_writer = csv.writer(stdout)
+    csv_writer.writerow( ('period start', 'period end', 'debits', 'credits') )
+    
+    def generate_detail_rows(values):
+        return (
+            ('', '', '', '', trans.GetDescription(),
+             gnc_numeric_to_python_Decimal(split.GetAmount()))
+            for trans, split in values )
+            
+
+    for start_date, end_date, debits, credits, debit_sum, credit_sum in \
+            period_list:
+        csv_writer.writerow( (start_date, end_date, debit_sum, credit_sum) )
+
+        if debits_show and len(debits) > 0:
+            csv_writer.writerow(
+                ('DEBITS', '', '', '', 'description', 'value') )
+            csv_writer.writerows( generate_detail_rows(debits) )
+            csv_writer.writerow( () )
+        if credits_show and len(credits) > 0:
+            csv_writer.writerow(
+                ('CREDITS', '', '', '', 'description', 'value') )
+            csv_writer.writerows( generate_detail_rows(credits) )
+            csv_writer.writerow( () )
+
+    # no save needed, we're just reading..
+    gnucash_session.end()
+
+
+if __name__ == "__main__": main()
+
+

Added: gnucash/trunk/src/optional/python-bindings/example_scripts/new_book_with_opening_balances.py
===================================================================
--- gnucash/trunk/src/optional/python-bindings/example_scripts/new_book_with_opening_balances.py	                        (rev 0)
+++ gnucash/trunk/src/optional/python-bindings/example_scripts/new_book_with_opening_balances.py	2010-06-30 19:26:57 UTC (rev 19314)
@@ -0,0 +1,313 @@
+#!/usr/bin/env python
+
+# new_book_with_opening_balances.py -- Replicate the account structure of a
+# book and apply basis opening balances from the original
+#
+# Copyright (C) 2009, 2010 ParIT Worker Co-operative <transparency at parit.ca>
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, contact:
+# Free Software Foundation           Voice:  +1-617-542-5942
+# 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+# Boston, MA  02110-1301,  USA       gnu at gnu.org
+#
+# @author Mark Jenkins, ParIT Worker Co-operative <mark at parit.ca>
+
+from gnucash import Session, Account, Transaction, Split, GncNumeric
+from gnucash.gnucash_core_c import \
+    GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT, \
+    ACCT_TYPE_ASSET, ACCT_TYPE_BANK, ACCT_TYPE_CASH, ACCT_TYPE_CHECKING, \
+    ACCT_TYPE_CREDIT, ACCT_TYPE_EQUITY, ACCT_TYPE_EXPENSE, ACCT_TYPE_INCOME, \
+    ACCT_TYPE_LIABILITY, ACCT_TYPE_MUTUAL, ACCT_TYPE_PAYABLE, \
+    ACCT_TYPE_RECEIVABLE, ACCT_TYPE_STOCK, ACCT_TYPE_ROOT, ACCT_TYPE_TRADING
+
+from sys import argv
+from os.path import abspath
+from datetime import date
+
+# This script takes a gnucash url
+# and creates a new file/db at a second url that has the same
+# account tree and an equivilent opening balance on all the simple balance
+# sheet accounts (not for income and expense accounts)
+#
+# This is done a per currency basis, one opening balance account for each
+#
+# For non-simple balance sheet accounts (like payable, recievable, stock,
+# mutual, and trading, you'll have to put the opening balance in yourself
+#
+# Invocation examples:
+# gnucash-env python new_book_with_opening_balances.py \
+#   'sqlite3:///home/mark/test.gnucash'
+#   'sqlite3:///home/mark/new_test.gnucash'
+#
+# gnucash-env python new_book_with_opening_balances.py \
+#   'sqlite3:///home/mark/test.gnucash' \
+#   'xml:///crypthome/mark/parit-financial-system/new_test.gnucashxml'
+#
+# Remember that the gnucash python package has to be in your PYTHONPATH
+# if you're installed GnuCash in a non-standard location, you'll have to do
+# something like this
+# export PYTHONPATH=gnucash_install_path/lib/python2.x/site-packages/
+
+
+OPENING_DATE = (1, 1, 2011) # day, month, year
+
+# possible acccount types of interest for opening balances
+ACCOUNT_TYPES_TO_OPEN = set( (
+        ACCT_TYPE_BANK,
+        ACCT_TYPE_CASH,
+        ACCT_TYPE_CREDIT,
+        ACCT_TYPE_ASSET,
+        ACCT_TYPE_LIABILITY,
+        ACCT_TYPE_STOCK,
+        ACCT_TYPE_MUTUAL,
+        ACCT_TYPE_INCOME,
+        ACCT_TYPE_EXPENSE,
+        ACCT_TYPE_EQUITY,
+        ACCT_TYPE_RECEIVABLE,
+        ACCT_TYPE_PAYABLE,
+        ACCT_TYPE_TRADING,
+))
+
+# You don't need an opening balance for income and expenses, past income
+# and expenses should be in Equity->retained earnings
+# so we remove them from the above set
+ACCOUNT_TYPES_TO_OPEN = ACCOUNT_TYPES_TO_OPEN.difference( set((
+            ACCT_TYPE_INCOME,
+            ACCT_TYPE_EXPENSE,
+            )) )
+
+# This script isn't capable of properly creating the lots required for
+# STOCK, MUTUAL, RECEIVABLE, and PAYABLE -- you'll have to create opening
+# balances for them manually; so they are not included in the set for
+# opening balances
+ACCOUNT_TYPES_TO_OPEN = ACCOUNT_TYPES_TO_OPEN.difference( set((
+            ACCT_TYPE_STOCK,
+            ACCT_TYPE_MUTUAL,
+            ACCT_TYPE_RECEIVABLE,
+            ACCT_TYPE_PAYABLE,
+            )) )
+
+# this script isn't capable of properly setting up the transactions for
+# ACCT_TYPE_TRADING, you'll have to create opening balances for them mannually;
+# so, they are not included in the set of accounts used for opening balances
+ACCOUNT_TYPES_TO_OPEN.remove(ACCT_TYPE_TRADING)
+
+OPENING_BALANCE_ACCOUNT = ( 'Equity', 'Opening Balances')
+
+# if possible, this program will try to use the account above for the
+# currency listed below, and a variation of the above
+# Equity->"Opening Balances Symbol" for all other currencies
+PREFERED_CURRENCY_FOR_SIMPLE_OPENING_BALANCE = ("CURRENCY", "CAD")
+
+def initialize_split(book, value, account, trans):
+    split = Split(book)
+    split.SetValue(value)
+    split.SetAccount(account)
+    split.SetParent(trans)
+    return split
+
+
+def record_opening_balance(original_account, new_account, new_book,
+                           opening_balance_per_currency, commodity_tuple
+                           ):
+    # create an opening balance if the account type is right
+    if new_account.GetType() in ACCOUNT_TYPES_TO_OPEN:
+        final_balance = original_account.GetBalance()
+        if final_balance.num() != 0:
+            # if there is a new currency type, associate with the currency
+            # a Transaction which will be the opening transaction for that
+            # currency and a GncNumeric value which will be the opening
+            # balance acccount amount
+            if commodity_tuple not in opening_balance_per_currency:
+                trans = Transaction(new_book)
+                opening_balance_per_currency[commodity_tuple] = (
+                    Transaction(new_book), GncNumeric(0, 1) )
+            trans, total = opening_balance_per_currency[commodity_tuple]
+
+            new_total = total.sub(
+                final_balance,
+                GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT )
+            
+            initialize_split(
+                new_book,
+                final_balance,
+                new_account, trans)
+            opening_balance_per_currency[commodity_tuple] = \
+                (trans, new_total )
+
+def recursivly_build_account_tree(original_parent_account,
+                                  new_parent_account,
+                                  new_book,
+                                  new_commodity_table,
+                                  opening_balance_per_currency,
+                                  account_types_to_open ):
+
+    for child in original_parent_account.get_children():
+        original_account = Account(instance=child)
+        new_account = Account(new_book)
+        # attach new account to its parent
+        new_parent_account.append_child(new_account)
+
+        # copy simple attributes
+        for attribute in ('Name', 'Type', 'Description', 'Notes',
+                          'Code', 'TaxRelated', 'Placeholder'):
+            # new_account.SetAttribute( original_account.GetAttribute() )
+            getattr(new_account, 'Set' + attribute)(
+                getattr(original_account, 'Get' + attribute)() )
+
+        # copy commodity
+        orig_commodity = original_account.GetCommodity()
+        namespace = orig_commodity.get_namespace()
+        mnemonic = orig_commodity.get_mnemonic()
+        new_commodity = new_commodity_table.lookup(namespace, mnemonic)
+        assert(new_commodity.get_instance() != None )
+        new_account.SetCommodity(new_commodity)
+
+        record_opening_balance( original_account, new_account,
+                                new_book, opening_balance_per_currency,
+                                (namespace, mnemonic),
+                                )
+
+        recursivly_build_account_tree(original_account,
+                                      new_account,
+                                      new_book,
+                                      new_commodity_table,
+                                      opening_balance_per_currency,
+                                      account_types_to_open)
+
+def reconstruct_account_name_with_mnemonic(account_tuple, mnemonic):
+    opening_balance_account_pieces = list(account_tuple)
+    opening_balance_account_pieces[
+        len(opening_balance_account_pieces) - 1 ] += " - " + mnemonic    
+    return opening_balance_account_pieces
+
+def find_or_make_account(account_tuple, root_account, book,
+                         currency ):
+    current_account_name, account_path = account_tuple[0], account_tuple[1:]
+    current_account = root_account.lookup_by_name(current_account_name)
+    if current_account.get_instance() == None:
+        current_account = Account(book)
+        current_account.SetName(current_account_name)
+        current_account.SetCommodity(currency)
+        root_account.append_child(current_account)
+    
+    if len(account_path) > 0:
+        return find_or_make_account(account_path, current_account, book,
+                                    currency)
+    else:
+        account_commod = current_account.GetCommodity()
+        if (account_commod.get_mnemonic(),
+            account_commod.get_namespace() ) == \
+            (currency.get_mnemonic(),
+             currency.get_namespace()) :
+            return current_account
+        else:
+            return None
+
+def choke_on_none_for_no_account(opening_account, extra_string ):
+    if opening_account == None:
+        raise Exception("account currency and name mismatch, " + extra_string)
+
+def create_opening_balance_transaction(commodtable, namespace, mnemonic,
+                                       new_book_root, new_book,
+                                       opening_trans, opening_amount,
+                                       simple_opening_name_used):
+    currency = commodtable.lookup(namespace, mnemonic)
+    assert( currency.get_instance() != None )
+
+    if simple_opening_name_used:
+        account_pieces = reconstruct_account_name_with_mnemonic(
+            OPENING_BALANCE_ACCOUNT,
+            mnemonic)
+        opening_account = find_or_make_account(
+            account_pieces, new_book_root, new_book, currency )
+        choke_on_none_for_no_account(opening_account,
+                                     ', '.join(account_pieces) )
+    else:
+        opening_account = find_or_make_account(OPENING_BALANCE_ACCOUNT,
+                                               new_book_root, new_book,
+                                               currency )
+        simple_opening_name_used = True
+        if opening_account == None:
+            account_pieces = reconstruct_account_name_with_mnemonic(
+                OPENING_BALANCE_ACCOUNT,
+                mnemonic)
+            opening_account = find_or_make_account(
+                account_pieces, new_book_root, new_book, currency )
+            choke_on_none_for_no_account(opening_account,
+                                             ', '.join(account_pieces) )
+
+    # we don't need to use the opening balance account at all if all
+    # the accounts being given an opening balance balance out
+    if opening_amount.num() != 0:
+        initialize_split(new_book, opening_amount, opening_account,
+                         opening_trans)
+
+    opening_trans.SetDate( *OPENING_DATE )
+    opening_trans.SetCurrency(currency)
+    opening_trans.SetDescription("Opening Balance")
+
+    return simple_opening_name_used
+        
+def main():
+    original_book_session = Session(argv[1], False)
+    new_book_session = Session(argv[2], True)
+    new_book = new_book_session.get_book()
+    new_book_root = new_book.get_root_account()
+
+    commodtable = new_book.get_table()
+    # we discovered that if we didn't have this save early on, there would
+    # be trouble later
+    new_book_session.save()
+
+    opening_balance_per_currency = {}
+    recursivly_build_account_tree(
+        original_book_session.get_book().get_root_account(),
+        new_book_root,
+        new_book,
+        commodtable,
+        opening_balance_per_currency,
+        ACCOUNT_TYPES_TO_OPEN
+        )
+
+    (namespace, mnemonic) = PREFERED_CURRENCY_FOR_SIMPLE_OPENING_BALANCE
+    if (namespace, mnemonic) in opening_balance_per_currency:
+        opening_trans, opening_amount = opening_balance_per_currency[
+            (namespace, mnemonic)]
+        simple_opening_name_used = create_opening_balance_transaction(
+            commodtable, namespace, mnemonic,
+            new_book_root, new_book,
+            opening_trans, opening_amount,
+            False )
+        del opening_balance_per_currency[
+            PREFERED_CURRENCY_FOR_SIMPLE_OPENING_BALANCE]
+    else:
+        simple_opening_name_used = False
+
+    for (namespace, mnemonic), (opening_trans, opening_amount) in \
+            opening_balance_per_currency.iteritems() :
+        simple_opening_name_used = create_opening_balance_transaction(
+            commodtable, namespace, mnemonic,
+            new_book_root, new_book,
+            opening_trans, opening_amount,
+            simple_opening_name_used )
+
+    new_book_session.save()
+    new_book_session.end()
+    original_book_session.end()
+    
+
+if __name__ == "__main__":
+    main()
+
+

Added: gnucash/trunk/src/optional/python-bindings/example_scripts/simple_sqlite_create.py
===================================================================
--- gnucash/trunk/src/optional/python-bindings/example_scripts/simple_sqlite_create.py	                        (rev 0)
+++ gnucash/trunk/src/optional/python-bindings/example_scripts/simple_sqlite_create.py	2010-06-30 19:26:57 UTC (rev 19314)
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+
+from gnucash import Session, Account
+from os.path import abspath
+from gnucash.gnucash_core_c import ACCT_TYPE_ASSET
+
+s = Session('sqlite3://%s' % abspath('test.blob'), True)
+# this seems to make a difference in more complex cases
+s.save()
+
+book = s.book
+root = book.get_root_account()
+a = Account(book)
+root.append_child(a)
+a.SetName('wow')
+a.SetType(ACCT_TYPE_ASSET)
+
+commod_table = book.get_table()
+a.SetCommodity( commod_table.lookup('CURRENCY', 'CAD') )
+s.save()
+
+s.end()



More information about the gnucash-changes mailing list