mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
Nice example python scripts, patch by Mark Jenkins.
git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@19314 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
parent
eaf3c5cbf8
commit
c84e7c381c
@ -55,6 +55,10 @@ EXTRA_DIST = \
|
||||
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
|
||||
|
258
src/optional/python-bindings/example_scripts/account_analysis.py
Normal file
258
src/optional/python-bindings/example_scripts/account_analysis.py
Normal file
@ -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@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@gnu.org
|
||||
#
|
||||
# @author Mark Jenkins, ParIT Worker Co-operative <mark@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()
|
||||
|
||||
|
@ -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@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@gnu.org
|
||||
#
|
||||
# @author Mark Jenkins, ParIT Worker Co-operative <mark@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()
|
||||
|
||||
|
@ -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()
|
Loading…
Reference in New Issue
Block a user