コード例 #1
0
def main():
    args = parse_cmdline()
    if args.version:
        print VERSION
        exit(0)

    if args.verbose:
        loglevel = logging.DEBUG
    elif args.quiet:
        loglevel = logging.WARN
    else:
        loglevel = logging.INFO
    logging.basicConfig(level=loglevel)

    rules = readrules(args.rulesfile)
    account_path = re.split(':', args.ac2fix)

    gnucash_session = Session(args.gnucash_file, is_new=False)
    total = 0
    imbalance = 0
    fixed = 0
    try:
        root_account = gnucash_session.book.get_root_account()
        orig_account = account_from_path(root_account, account_path)

        imbalance_pattern = re.compile(args.imbalance_ac)

        for split in orig_account.GetSplitList():
            total += 1
            trans = split.parent
            splits = trans.GetSplitList()
            trans_date = date.fromtimestamp(trans.GetDate())
            trans_desc = trans.GetDescription()
            trans_memo = trans.GetNotes()
            for split in splits:
                ac = split.GetAccount()
                acname = ac.GetName()
                logging.debug('%s: %s => %s', trans_date, trans_desc, acname)
                if imbalance_pattern.match(acname):
                    imbalance += 1
                    search_str = trans_desc
                    if args.use_memo:
                        search_str = trans_memo
                    newac = get_ac_from_str(search_str, rules, root_account)
                    if newac != "":
                        logging.debug('\tChanging account to: %s',
                                      newac.GetName())
                        split.SetAccount(newac)
                        fixed += 1

        if not args.nochange:
            gnucash_session.save()

        logging.info('Total splits=%s, imbalance=%s, fixed=%s', total,
                     imbalance, fixed)

    except Exception as ex:
        logging.error(ex)

    gnucash_session.end()
コード例 #2
0
def write_transactions_to_gnucash(gnucash_file,
                                  currency,
                                  all_items,
                                  dry_run=False,
                                  date_from=None):
    logging.debug('Opening GnuCash file %s..', gnucash_file)
    session = Session(gnucash_file)
    book = session.book
    commod_tab = book.get_table()
    currency = commod_tab.lookup('ISO4217', currency)

    if date_from:
        date_from = datetime.datetime.strptime(date_from, '%Y-%m-%d')

    imported_items = set()
    for item in all_items:
        if date_from and item.date < date_from:
            logging.info('Skipping entry %s (%s)',
                         item.date.strftime('%Y-%m-%d'), item.split_amount)
            continue
        if item.as_tuple() in imported_items:
            logging.info('Skipping entry %s (%s) --- already imported!',
                         item.date.strftime('%Y-%m-%d'), item.split_amount)
            continue
        add_transaction(book, item, currency)
        imported_items.add(item.as_tuple())

    if dry_run:
        logging.debug('** DRY-RUN **')
    else:
        logging.debug('Saving GnuCash file..')
        session.save()
    session.end()
コード例 #3
0
def write_transactions_to_gnucash(gnucash_file, currency, all_items, dry_run=False, date_from=None):
    logging.debug("Opening GnuCash file %s..", gnucash_file)
    session = Session(gnucash_file)
    book = session.book
    commod_tab = book.get_table()
    currency = commod_tab.lookup("ISO4217", currency)

    if date_from:
        date_from = datetime.datetime.strptime(date_from, "%Y-%m-%d")

    imported_items = set()
    for item in all_items:
        if date_from and item.date < date_from:
            logging.info("Skipping entry %s (%s)", item.date.strftime("%Y-%m-%d"), item.split_amount)
            continue
        if item.as_tuple() in imported_items:
            logging.info(
                "Skipping entry %s (%s) --- already imported!", item.date.strftime("%Y-%m-%d"), item.split_amount
            )
            continue
        add_transaction(book, item, currency)
        imported_items.add(item.as_tuple())

    if dry_run:
        logging.debug("** DRY-RUN **")
    else:
        logging.debug("Saving GnuCash file..")
        session.save()
    session.end()
コード例 #4
0
def main(args):
    if args.verbose:
        lvl = logging.DEBUG
    elif args.quiet:
        lvl = logging.WARN
    else:
        lvl = logging.INFO

    logging.basicConfig(level=lvl)

    all_items = []
    for fn in args.file:
        logging.debug('Reading %s..', fn)
        with open(fn) as fd:
            items = qif.parse_qif(fd)
            logging.debug('Read %s items from %s..', len(items), fn)
            all_items.extend(items)

    logging.debug('Opening GnuCash file %s..', args.gnucash_file)
    session = Session(args.gnucash_file)
    book = session.book
    commod_tab = book.get_table()
    currency = commod_tab.lookup('ISO4217', args.currency)

    for item in all_items:
        add_transaction(book, item, currency)    

    logging.debug('Saving GnuCash file..')
    session.save()
    session.end()
コード例 #5
0
ファイル: TestChangeParking.py プロジェクト: tjakway/Accregex
    def runTest(self):
        self.instrument_main()

        try:
            from accregex import Account
            from accregex.AccountUtil import gnc_numeric_to_python_Decimal
            from gnucash import Session
            session = Session(AccregexTest.reg_doc_example, is_new=False, ignore_lock=False)
            parking_expense_account = Account.get_account(session.book.get_root_account(), parking_expense_account_full_name)
            actual_balance = gnc_numeric_to_python_Decimal(parking_expense_account.GetBalance())
            expected_balance = Decimal(25)

            self.assertEqual(actual_balance, expected_balance)
            
            #TODO: check that the actual transaction in Assets:Current Assets:Checking Account was changed
            session.end()
            
            #put the gnucash file back the way we found it
            shutil.move(AccregexTest.reg_doc_example + ".bak", AccregexTest.reg_doc_example)
        except:
            #in case of error close the session and re-raise
            #equivalent to a "finally" block
            if "session" in locals():
                session.end()
            raise
コード例 #6
0
ファイル: make_securities.py プロジェクト: jethrogb/finance
def main():
    if len(argv) < 3:
        print "Usage: python make_securities.py <gnucash-file> <symbols-file>"
        exit(1)

    gnucash_file = argv[1]

    secs = read_symbols(argv[2])

    sess = Session(gnucash_file)

    try:
        # Make sure all the commodities exist
        ct = sess.book.get_table()

        created_secs = 0
        updated_secs = 0
        total_secs = 0
        for ns in ["CUSIP", "ISIN"]:
            total_secs += len(secs[ns])
            for c in ct.get_commodities(ns):
                matched_sec = None
                for k in secs[ns]:
                    sec = secs[ns][k]
                    if sec["id"] == c.get_cusip():
                        matched_sec = sec
                        break
                if matched_sec:
                    matched_sec["c"] = c
                    updated = False
                    if c.get_fullname() != matched_sec["name"]:
                        c.set_fullname(matched_sec["name"])
                        updated = True
                    if c.get_mnemonic() != matched_sec["symbol"]:
                        c.set_mnemonic(matched_sec["symbol"])
                        updated = True
                    if updated:
                        updated_secs += 1
                        print "DEBUG: Updating Commodity", sec["name"]
                    del secs[ns][matched_sec["id"]]
            for k in secs[ns]:
                sec = secs[ns][k]
                c = GncCommodity(sess.book, sec["name"], ns, sec["symbol"], sec["id"], 10000)
                if c:
                    ct.insert(c)
                    created_secs += 1
                    print "DEBUG: Created Commodity", sec["name"], sec["id"]
                else:
                    print "ERROR: Error creating Commodity", sec["name"]

        print "INFO:", total_secs, "commodities total,", created_secs, "created,", updated_secs, "updated."

        wait_for_backup_file(gnucash_file)
        sess.save()
    except BaseException as e:
        print "ERROR:", e
    finally:
        sess.end()
        sess.destroy()
コード例 #7
0
ファイル: test_commodity.py プロジェクト: zkw03/gnucash
class CommoditySession(TestCase):
    def setUp(self):
        self.ses = Session()
        self.book = self.ses.get_book()
        self.table = self.book.get_table()

    def tearDown(self):
        self.ses.end()
コード例 #8
0
def main():
    args = parse_cmdline()
    if args.version:
        print VERSION
        exit(0)

    if args.verbose:
        loglevel = logging.DEBUG
    elif args.quiet:
        loglevel = logging.WARN
    else:
        loglevel = logging.INFO
    logging.basicConfig(level=loglevel)

    rules = readrules(args.rulesfile)
    account_path = re.split(':', args.ac2fix)

    gnucash_session = Session(args.gnucash_file, is_new=False)
    total = 0
    imbalance = 0
    fixed = 0
    try:
        root_account = gnucash_session.book.get_root_account()
        orig_account = account_from_path(root_account, account_path)

        imbalance_pattern = re.compile(args.imbalance_ac)

        for split in orig_account.GetSplitList():
            total += 1
            trans = split.parent
            splits = trans.GetSplitList()
            trans_date = date.fromtimestamp(trans.GetDate())
            trans_desc = trans.GetDescription()
            trans_memo = trans.GetNotes()
            for split in splits:
                ac = split.GetAccount()
                acname = ac.GetName()
                logging.debug('%s: %s => %s', trans_date, trans_desc, acname)
                if imbalance_pattern.match(acname):
                    imbalance += 1
                    search_str = trans_desc
                    if args.use_memo:
                        search_str = trans_memo
                    newac = get_ac_from_str(search_str, rules, root_account)
                    if newac != "":
                        logging.debug('\tChanging account to: %s', newac.GetName())
                        split.SetAccount(newac)
                        fixed += 1

        if not args.nochange:
            gnucash_session.save()

        logging.info('Total splits=%s, imbalance=%s, fixed=%s', total, imbalance, fixed)

    except Exception as ex:
        logging.error(ex) 

    gnucash_session.end()
コード例 #9
0
class TestCommands(TestCase):
    def setUp(self):
        self.session = Session(
            os.path.join(os.path.dirname(os.path.abspath(__file__)),
                         "test.gnucash"))

    def tearDown(self):
        self.session.end()

    def test_create_split_transaction_vat(self):
        commands.create_split_transaction(
            self.session.book,
            settings.GNUCASH_BANK_ACCOUNT,
            "Internet",
            date.today(),
            "ADSL",
            Decimal("539.00"),
        )

    def test_create_split_transaction_novat(self):
        commands.create_split_transaction(
            self.session.book,
            settings.GNUCASH_BANK_ACCOUNT,
            "Internet",
            date.today(),
            "ADSL",
            Decimal("539.00"),
            False,
        )

    def test_pay_invoice(self):
        commands.pay_invoice(self.session.book, "000002", Decimal("99.99"),
                             date.today())

    def test_pay_invoice_exists(self):
        self.assertRaises(
            PaymentExists,
            commands.pay_invoice,
            self.session.book,
            "000001",
            Decimal("9.99"),
            date.today(),
        )

    def test_apply_payment(self):
        commands.apply_payment(self.session.book, "000001", Decimal("99.99"),
                               date.today())

    def test_apply_payment_exists(self):
        self.assertRaises(
            PaymentExists,
            commands.apply_payment,
            self.session.book,
            "000001",
            Decimal("9.99"),
            date(2019, 11, 21),
        )
コード例 #10
0
def account_choices(book=None):
    choices = [("", "---------")]
    if not book:
        session = Session(settings.GNUCASH_FILE)
        book = session.book
    for ac in queries.get_accounts(book.get_root_account()):
        choices.append((ac.name, ac.name))
    if "session" in locals():
        session.end()
    return choices
コード例 #11
0
def customer_choices(book=None):
    choices = [("", "---------")]
    if not book:
        session = Session(settings.GNUCASH_FILE)
        book = session.book
    for c in queries.get_customers(book):
        choices.append((c.GetID(), c.GetName()))
    if "session" in locals():
        session.end()
    return choices
コード例 #12
0
def find_av_main():
    exe = argv[0].split('/')[-1]
    if len(argv) < 6:
        print("NOT ENOUGH parameters!")
        print("usage: {} <book url> <year> <month> <day> <space-separated path to the account of interest>".format(exe))
        print("PROGRAM EXIT!")
        return

    print("\nrunning {} at run-time: {}\n".format(exe, str(datetime.now())))

    global gnucash_session
    try:
        (gnucash_file, str_year, str_month, str_day) = argv[1:5]
        print("find asset values in {} on {}-{}-{}".format(gnucash_file, str_year, str_month, str_day))

        val_year, val_month, val_day = [int(blah) for blah in (str_year, str_month, str_day)]
        date_of_interest = date(val_year, val_month, val_day)

        account_path = argv[5:]
        print("account_path = {}\n".format(str(account_path)))

        gnucash_session = Session(gnucash_file, is_new=False)
        book = gnucash_session.book

        commod_tab = book.get_table()
        # noinspection PyPep8Naming
        CAD = commod_tab.lookup("ISO4217", "CAD")

        root_account = book.get_root_account()

        account_of_interest = account_from_path(root_account, account_path)
        acct_name = account_of_interest.GetName()

        total = get_asset_balance(account_of_interest, date_of_interest, CAD)

        # get the list of all descendant accounts
        descendants = account_of_interest.get_descendants()

        if len(descendants) > 0:
            # get the values for EACH sub-account too
            print("\nDescendants of {}:".format(acct_name))
            for subAcct in descendants:
                total += get_asset_balance(subAcct, date_of_interest, CAD)

        print("total {} value = {}${}".format(acct_name, CAD.get_mnemonic(), total))

        # no save needed, we're just reading...
        gnucash_session.end()

    except Exception as ae:
        print("Exception: {}".format(ae))
        if "gnucash_session" in locals() and gnucash_session is not None:
            gnucash_session.end()

    print("\n >>> PROGRAM ENDED.")
コード例 #13
0
    def setUp(self):
        self.util = Util()
        self.account = Nubank(self.util.DEFAULT_ACCOUNT_SRC_FILE)
        self.ledger = Ledger(self.account, self.util.DEFAULT_CURRENCY, False,
                             self.util.DEFAULT_GNUCASH_FILE)

        session = Session(self.util.DEFAULT_GNUCASH_FILE)
        self.book = session.book
        self.currency = self.book.get_table().lookup(
            'ISO4217', self.util.DEFAULT_CURRENCY)
        session.end()
コード例 #14
0
ファイル: gnucashBook.py プロジェクト: SAL-e/mint2gnucash
class GnucashBook(object):
    """docstring for GnucashBook."""
    def __init__(self, file, currency, is_new=False):
        self.session = Session(file, is_new)
        try:
            self.book = self.session.book
            self.commod_tab = self.book.get_table()
            self.currency = self.commod_tab.lookup('ISO4217', currency)
            self.root = self.book.get_root_account()
        except Exception as ex:
            pass
#            logging.error(ex)

    def lookup_account_by_path(self, root, path):
        acc = root.lookup_by_name(path[0])
        if acc.get_instance() == None:
            raise Exception('Account path {} not found'.format(':'.join(path)))
        if len(path) > 1:
            return GnucashBook.lookup_account_by_path(self, acc, path[1:])
        return acc

    def lookup_account(self, name):
        path = name.split(':')
        return GnucashBook.lookup_account_by_path(self, self.root, path)

    def write_transactions(self, transactions):
        for transaction in transactions:

            tx = Transaction(self.book)
            tx.BeginEdit()
            tx.SetCurrency(self.currency)
            tx.SetDateEnteredTS(datetime.datetime.now())
            tx.SetDatePostedTS(transaction.datetime)
            tx.SetDescription(transaction.description)
            tx.SetNotes(transaction.note)

            for split in transaction.splits:
                sp = Split(self.book)
                sp.SetParent(tx)
                sp.SetAccount(GnucashBook.lookup_account(self, split.account))
                sp.SetMemo(split.memo)
                amount = int(
                    Decimal(split.amount) * self.currency.get_fraction())
                sp.SetValue(GncNumeric(amount, self.currency.get_fraction()))
                sp.SetAmount(GncNumeric(amount, self.currency.get_fraction()))

            tx.CommitEdit()

    def close(self, nochange):
        if not nochange:
            self.session.save()
        self.session.end()
コード例 #15
0
ファイル: gnucashdata.py プロジェクト: waldenven/tsdata
 def _loadarchive(self):
     if self._cache is not None:
         return
     session      = Session(self.url, True, False, False)
     root         = session.book.get_root_account()
     book         = session.book
     account      = book.get_root_account()
     commod_table = book.get_table()
     pdb          = book.get_price_db()
     df           = full_price_list(commod_table, pdb)
     df['value']  = df['num'] * 1.0 / df['denom']
     session.end()
     session.destroy()
     self._cache  = dropdupe(df, ['date', 'namespace', 'commod'])
コード例 #16
0
def main():

    if len(argv) < 3:
        print 'not enough parameters'
        print 'usage: new_book_with_opening_balances.py {source_book_url} {destination_book_url}'
        print 'examples:'
        print "gnucash-env python new_book_with_opening_balances.py '/home/username/test.gnucash' 'sqlite3:///home/username/new_test.gnucash'"
        print "gnucash-env python new_book_with_opening_balances.py '/home/username/test.gnucash' 'xml:///crypthome/username/finances/new_test.gnucash'"
        return

    #have everything in a try block to unable us to release our hold on stuff to the extent possible
    try:
        original_book_session = Session(argv[1], is_new=False)
        new_book_session = Session(argv[2], is_new=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()
    except:
        if "original_book_session" in locals():
            original_book_session.end()

        if "new_book_session" in locals():
            new_book_session.end()

        raise
コード例 #17
0
def main():
	args = parse_cmdline()
	if args.version:
		print VERSION
		exit(0)
	rules = readrules(args.rulesfile)
	account_path = re.split(':', args.ac2fix)
	

	gnucash_session = Session(args.gnucash_file, is_new=False)
	root_account = gnucash_session.book.get_root_account()
	orig_account = account_from_path(root_account, account_path)

	total = 0
	imbalance = 0
	fixed = 0
	for split in orig_account.GetSplitList():
		total += 1
		trans = split.parent
		splits = trans.GetSplitList()
		trans_date = date.fromtimestamp(trans.GetDate())
		trans_desc = trans.GetDescription()
		trans_memo = trans.GetNotes()
		ac = splits[0].GetAccount()
		acname = ac.GetName()
		if not args.silent:
			print trans_date,":", trans_desc, "=>", acname
		# check if acname is "Imbalance-USD"
		if acname == args.imbalance_ac:
			imbalance += 1
			search_str = trans_desc
			if args.use_memo:
				search_str = trans_memo
			newac = get_ac_from_str(search_str, rules, root_account)
			if newac != "":
				if not args.silent:
					print "\t Changing account to: ", newac.GetName()
				splits[0].SetAccount(newac)
				fixed += 1
 
	if not args.nochange:
		gnucash_session.save()
	gnucash_session.end()

	if not args.silent:
		print "Total splits=", total, " imbalance=", imbalance, " fixed=", fixed
コード例 #18
0
 def handle(self, *args, **options):
     try:
         s = Session(settings.GNUCASH_FILE)
         book = s.book
         with open(args[0], "r") as f:
             reader = csv.DictReader(f)
             for row in reader:
                 number = row["invoice"]
                 amount = Decimal(row["amount"])
                 date = parser.parse(row["date"])
                 try:
                     pay_invoice(book, number, amount, date)
                 except PaymentExists as e:
                     print("%s... skipping" % e)
         s.save()
     finally:
         s.end()
コード例 #19
0
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()
コード例 #20
0
class Accounting:

    def __init__(self,filename):
        self.closed = False

        self.session = Session(filename)
        self.book = self.session.book
        commod_table = self.book.get_table()
        self.EUR = commod_table.lookup('CURRENCY', 'EUR')
        
        self.receivables = self._get_account(ACCOUNT_RECEIVABLE,ACCT_TYPE_RECEIVABLE);
        self.payables = self._get_account(ACCOUNT_PAYABLE,ACCT_TYPE_PAYABLE);
        
        self.btw_collected_high = self._get_account(ACCOUNT_BTW_COLLECTED_HIGH,ACCT_TYPE_LIABILITY);
        self.btw_reported_high = self._get_account(ACCOUNT_BTW_REPORTED_HIGH,ACCT_TYPE_LIABILITY);
        self.btw_collected_low = self._get_account(ACCOUNT_BTW_COLLECTED_LOW,ACCT_TYPE_LIABILITY);
        self.btw_reported_low = self._get_account(ACCOUNT_BTW_REPORTED_LOW,ACCT_TYPE_LIABILITY);
        self.btw_collected_import = self._get_account(ACCOUNT_BTW_COLLECTED_IMPORT,ACCT_TYPE_LIABILITY);
        self.btw_reported_import = self._get_account(ACCOUNT_BTW_REPORTED_IMPORT,ACCT_TYPE_LIABILITY);
        self.btw_paid_nl = self._get_account(ACCOUNT_BTW_PAID_NL,ACCT_TYPE_ASSET);
        self.btw_reported_nl = self._get_account(ACCOUNT_BTW_REPORTED_NL,ACCT_TYPE_ASSET);
        self.btw_paid_eu = self._get_account(ACCOUNT_BTW_PAID_EU,ACCT_TYPE_ASSET);
        self.btw_reported_eu = self._get_account(ACCOUNT_BTW_REPORTED_EU,ACCT_TYPE_ASSET);
        self.btw_reported_eu = self._get_account(ACCOUNT_BTW_REPORTED_EU,ACCT_TYPE_ASSET);
        self.tax_expense = self._get_account(ACCOUNT_TAX_EXPENSE,ACCT_TYPE_EXPENSE);

        self.taxtables = self.book.TaxTableGetTables()

    def _get_account(self,name,acct_type):
        root = self.book.get_root_account()
        acct = root.lookup_by_full_name(name)
        if acct.GetType() != acct_type:
            raise Exception('Account '+name+' does not exist with type '+str(acct_type))
        return acct

    def save(self):
        self.session.save()

    def close(self):
        if not self.closed:
            self.session.end()
            self.closed = True

    def __del__(self):
        self.close()
コード例 #21
0
    def prepare_session(self):
        """
        initialization needed for a Gnucash session
        :return: message
        """
        self.logger.print_info("prepare_session()", BLUE)
        msg = TEST
        try:
            session = Session(self.gnc_file)
            self.book = session.book

            owner = self.monarch_record.get_owner()
            self.logger.print_info("Owner = {}".format(owner), GREEN)
            self.set_gnc_rec(InvestmentRecord(owner))

            self.create_gnucash_info()

            if self.mode == PROD:
                self.logger.print_info("Mode = {}: COMMIT Price DB edits and Save session.".format(self.mode), GREEN)

                if self.domain != TRADE:
                    self.price_db.commit_edit()

                # only ONE session save for the entire run
                session.save()

            session.end()
            session.destroy()

            msg = self.logger.get_log()

        except Exception as se:
            msg = "prepare_session() EXCEPTION!! '{}'".format(repr(se))
            self.logger.print_error(msg)
            if "session" in locals() and session is not None:
                session.end()
                session.destroy()
            raise se

        return msg
コード例 #22
0
def main():
    original_book_session = Session(argv[1], is_new=False)
    new_book_session = Session(argv[2], in_new=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()
コード例 #23
0
    def prepare_session(self):
        """
        Take the information from a transaction collection and produce Gnucash transactions to write to a Gnucash file
        :return: message
        """
        print_info("prepare_session()", MAGENTA)
        msg = TEST
        try:
            session = Session(self.gnc_file)
            self.book = session.book

            print_info("Owner = {}".format(self.tx_coll[OWNER]), GREEN)
            self.report_info = InvestmentRecord(self.tx_coll[OWNER])

            self.create_gnucash_info()

            if self.mode == PROD:
                msg = "Mode = {}: COMMIT Price DB edits and Save session.".format(
                    self.mode)
                print_info(msg, GREEN)
                self.price_db.commit_edit()
                # only ONE session save for the entire run
                session.save()

            session.end()
            session.destroy()

        except Exception as e:
            msg = "prepare_session() EXCEPTION!! '{}'".format(repr(e))
            print_error(msg)
            if "session" in locals() and session is not None:
                session.end()
                session.destroy()
            raise

        return msg
コード例 #24
0
class DenhacGncSession:
    _path = None
    _session = None
    _root = None
    _commod = None
    _currency = None

    def __init__(self, path):
        if os.path.exists(path + '.LCK'):
            raise AssertionError("""Lock file exists. Is GNUCash running?\n""")

        self._path = path
        with warnings.catch_warnings():
            warnings.simplefilter("ignore")
            self._session = Session(path, is_new = False)

        self._root = self.getBook().get_root_account()
        self._commod = self.getBook().get_table()
        self._currency = self._commod.lookup('CURRENCY', 'USD')

    def saveAndEndSession(self):
        self._session.save()
        self._session.end()

    def saveSession(self):
        self._session.save()

    def endSession(self):
        self._session.end()

    def cancelSession(self):
        self._session.end()
        self._session.destroy()

    def getBook(self):
        return self._session.book

    def getRoot(self):
        return self._session.book.get_root_account()

    def getCurrency(self):
        return self._currency
コード例 #25
0
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()
コード例 #26
0
def main():

    if len(argv) < 10:
        print('not enough parameters')
        print('usage: account_analysis.py {book url} {start year} {start month, numeric} {period type: monthly, quarterly, or yearly} {number of periods to show, from start year and month} {whether to show debits: debits-show for true, all other values false} {whether to show credits: credits-show for true, all other values false} {space separated account path, as many nested levels as desired} ')
        print('examples:\n')
        print("The following example analyzes 12 months of 'Assets:Test Account' from /home/username/test.gnucash, starting in January of 2010, and shows both credits and debits")
        print("gnucash-env python account_analysis.py '/home/username/test.gnucash' 2010 1 monthly 12 debits-show credits-show Assets 'Test Account'\n")
        print("The following example analyzes 2 quarters of 'Liabilities:First Level:Second Level' from /home/username/test.gnucash, starting March 2011, and shows credits but not debits")
        print("gnucash-env python account_analysis.py '/home/username/test.gnucash' 2011 3 quarterly 2 debits-noshow credits-show Liabilities 'First Level' 'Second Level")
        return

    try:
        (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(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():
            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()
    except:
        if "gnucash_session" in locals():
            gnucash_session.end()

        raise
コード例 #27
0
#!/usr/bin/env python3
##  @file
#   @brief Example Script simple sqlite create 
#   @ingroup python_bindings_examples

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'), is_new=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()
コード例 #28
0
                            trans.CommitEdit()
                            tx_guid = trans.GetGUID().to_string()

                        tx = models.ImportedTransaction()
                        tx.account_guid = acct_guid
                        tx.tx_guid = tx_guid
                        tx.source_tx_id = txinfo['sourceId']
                        imported_transactions.append(tx)

                u = models.Update()
                u.account_guid = acct_guid
                u.updated = datetime.utcnow()
                u.balance = balance
                u.save()

                for tx in imported_transactions:
                    tx.update = u
                    tx.save()

        f.close()

finally:
    debug_print('Ending GnuCash session')
    session.end()
    debug_print('Destroying GnuCash session')
    session.destroy()
    debug_print('Destroyed GnuCash session')

debug_print('Done importing JSON file(s)')
コード例 #29
0
# choose the account code to select
TARGET_ACCOUNT_CODE = '1234'

def mark_account_with_code_as_tax_related(account, target_code):
    """Looks at account to see if it has the target_account_code, if so
    set the account tax related flag to True and return True.
    If not, recursively tries to do the same to all children accounts
    of account.
    Returns False when recursion fails to find it.
    """
    if account.GetCode() == target_code:
        account.SetTaxRelated(True)
        return True
    else:
        for child in account.get_children():
            child = Account(instance=child)
            if mark_account_with_code_as_tax_related(child, target_code):
                return True
        return False
            
# Change this path to your own
gnucash_session = Session("xml:///home/mark/python-bindings-help/test.xac")

mark_account_with_code_as_tax_related(
    gnucash_session.book.get_root_account(),
    TARGET_ACCOUNT_CODE)

gnucash_session.save()
gnucash_session.end()
コード例 #30
0
class MonarchQrepToGncPrices:
    def __init__(self, fmon, gnc_file, mode):
        self.prod = (mode == PROD)
        self.mon_file = fmon

        self.session = Session(gnc_file)
        self.book = self.session.book

        self.root = self.book.get_root_account()
        self.root.get_instance()

        self.price_db = self.book.get_price_db()

        commod_tab = self.book.get_table()
        self.currency = commod_tab.lookup("ISO4217", "CAD")

    def parse_monarch_qtrep(self):
        """
        PARSE FOR PRICES TO ADD TO THE PRICE DB
        loop:
            find: 'For the Period <date1> to <date2>' for the date for the prices
            find: MON_MARK or MON_LULU as OWNER
            find: 'Page 1' as the key to start finding prices
            find: 'OPEN...' or 'TFSA...' or 'RRSP...' as Plan Type
                  use that as the key for this section of the Tx_Collection
            find: '(match1) - (match2) (match3)...'
                    1) use match1 as Fund Company, match2 as Fund Code for the account
                    2) use match3 as Fund Company, match2 as Fund Code for the account
            find: '$price'
            find: 'Transaction Details' as key to search for next Plan Type
                OR: another match of 'Fund Company & Fund Code'
        :return: Configuration.InvestmentRecord object
        """
        print_info("parse_monarch_qtrep()\nRuntime = {}\n".format(strnow), MAGENTA)

        # re searches
        re_date    = re.compile(r"^For the period (.*) to (\w{3}) (\d{1,2}), (\d{4})")
        re_mark    = re.compile(".*({}).*".format(MON_MARK))
        re_lulu    = re.compile(".*({}).*".format(MON_LULU))
        re_start   = re.compile(r"^Page 1.*")
        re_comp1   = re.compile(r"^(.*) - ([0-9ATL]{3,5}).*")
        re_comp2   = re.compile(r"^(- )?(\d{3,5}) - (.*)")
        re_price   = re.compile(r"^\$([0-9,]{1,5})\.(\d{2,4}).*")
        re_plan    = re.compile(r"(OPEN|TFSA|RRSP)(\s?.*)")
        re_endplan = re.compile(r"^Transaction Details.*")
        re_finish  = re.compile(r"^Disclosure.*")

        mon_state = FIND_OWNER
        with open(self.mon_file) as fp:
            ct = 0
            for line in fp:
                ct += 1
                if mon_state == FIND_OWNER:
                    match_mark = re.match(re_mark, line)
                    match_lulu = re.match(re_lulu, line)
                    if match_mark or match_lulu:
                        if match_mark:
                            owner = match_mark.group(1)
                        elif match_lulu:
                            owner = match_lulu.group(1)
                        print_info("{}/ Owner: {}".format(ct, owner), RED)
                        tx_coll = InvestmentRecord(owner)
                        mon_state = FIND_START
                        continue

                if mon_state == FIND_START:
                    match_start = re.match(re_start, line)
                    if match_start:
                        print_info("{}/ Found Start!".format(ct), GREEN)
                        mon_state = FIND_DATE
                        continue

                if mon_state == FIND_DATE:
                    match_date = re.match(re_date, line)
                    if match_date:
                        day = match_date.group(3)
                        month = match_date.group(2)
                        year = match_date.group(4)
                        datestring = "{}-{}-{}".format(year, month, day)
                        pr_date = dt.strptime(datestring, '%Y-%b-%d')
                        tx_coll.set_date(pr_date)
                        print_info("date: {}".format(pr_date), CYAN)
                        mon_state = FIND_PLAN
                        continue

                if mon_state == FIND_PLAN:
                    match_finish = re.match(re_finish, line)
                    if match_finish:
                        print_info("{}/ FINISHED!".format(ct), RED)
                        break
                    match_plan = re.match(re_plan, line)
                    if match_plan:
                        plan_type = match_plan.group(1)
                        print_info("{}/ Plan type: {}".format(ct, plan_type), BLUE)
                        mon_state = FIND_COMPANY
                        continue

                if mon_state == FIND_COMPANY:
                    match_endsum = re.match(re_endplan, line)
                    if match_endsum:
                        print_info("{}/ END of '{}' plan.".format(ct, plan_type), BLUE)
                        mon_state = FIND_PLAN
                        continue
                    match_comp1 = re.match(re_comp1, line)
                    match_comp2 = re.match(re_comp2, line)
                    if match_comp1 or match_comp2:
                        if match_comp1:
                            company = match_comp1.group(1)
                            fund_code = match_comp1.group(2)
                        elif match_comp2:
                            company = match_comp2.group(3)
                            fund_code = match_comp2.group(2)
                        curr_tx = {FUND_CMPY: company, FUND_CODE: fund_code}
                        print_info("{}/ Fund is: '{}:{}'".format(ct, company, fund_code), MAGENTA)
                        mon_state = FIND_PRICE
                        continue

                if mon_state == FIND_PRICE:
                    match_price = re.match(re_price, line)
                    if match_price:
                        dollar_str = match_price.group(1)
                        cents_str = match_price.group(2)
                        print_info("{}/ price = '${}.{}'".format(ct, dollar_str, cents_str), GREEN)
                        curr_tx[DOLLARS] = dollar_str
                        curr_tx[CENTS] = cents_str
                        tx_coll.add_tx(plan_type, curr_tx)
                        mon_state = FIND_COMPANY
                        continue

        print_info("Found {} transactions.".format(tx_coll.get_size()))
        return tx_coll

    def get_prices_and_save(self, tx_coll):
        """
        create Gnucash prices, load and save to the Gnucash file's PriceDB
        :param tx_coll: InvestmentRecord object: transactions to use to extract Gnucash prices
        :return: message
        """
        print_info('get_prices_and_save()', MAGENTA)

        gncu = GncUtilities()

        msg = TEST
        self.price_db.begin_edit()
        print_info("self.price_db.begin_edit()", MAGENTA)
        try:
            for plan_type in tx_coll.plans:
                print_info("\n\nPlan type = {}".format(plan_type))
                for tx in tx_coll.plans[plan_type]:
                    base = pow(10, len(tx[CENTS]))
                    int_price = int(tx[DOLLARS] + tx[CENTS])
                    val = GncNumeric(int_price, base)

                    ast_parent_path = copy.copy(ACCT_PATHS[ASSET])
                    ast_parent_path.append(plan_type)

                    if plan_type != PL_OPEN:
                        if tx_coll.get_owner() == UNKNOWN:
                            raise Exception("PROBLEM!! Trying to process plan type '{}' but NO Owner information found"
                                            " in Tx Collection!!".format(plan_type))
                        ast_parent_path.append(ACCT_PATHS[tx_coll.get_owner()])

                    print_info("ast_parent_path = {}".format(str(ast_parent_path)), BLUE)
                    asset_parent = gncu.account_from_path(self.root, ast_parent_path)

                    # get the asset account name
                    name_key = tx[FUND_CMPY].split(' ')[0]
                    print_info("name_key = {}".format(name_key), YELLOW)
                    if name_key in FUND_NAME_CODE.keys():
                        name_code = FUND_NAME_CODE[name_key]
                        # special case
                        if name_code == ATL:
                            asset_acct_name = ATL_O59
                        else:
                            asset_acct_name = name_code + " " + tx[FUND_CODE]
                    else:
                        raise Exception("Could NOT find name key {}!".format(name_key))
                    print_info("asset_acct_name = {}".format(asset_acct_name), BLUE)

                    # special location for Trust Asset account
                    if asset_acct_name == TRUST_AST_ACCT:
                        asset_parent = self.root.lookup_by_name(TRUST)
                    print_info("asset_parent = {}".format(asset_parent.GetName()), BLUE)

                    # get the asset account
                    asset_acct = asset_parent.lookup_by_name(asset_acct_name)
                    if asset_acct is None:
                        # just skip updating cash-holding funds
                        if str(val) == '100000/10000':
                            continue
                        else:
                            raise Exception(
                                "Could NOT find acct '{}' under parent '{}'".format(asset_acct_name, asset_parent.GetName()))

                    print_info("Adding: {}[{}] @ ${}".format(asset_acct_name, tx_coll.get_date_str(), val), GREEN)

                    pr = GncPrice(self.book)
                    pr.begin_edit()
                    pr.set_time64(tx_coll.get_date())
                    comm = asset_acct.GetCommodity()
                    print_info("Commodity = {}:{}".format(comm.get_namespace(), comm.get_printname()), YELLOW)
                    pr.set_commodity(comm)

                    pr.set_currency(self.currency)
                    pr.set_value(val)
                    pr.set_source_string("user:price")
                    pr.set_typestr('last')
                    pr.commit_edit()

                    if self.prod:
                        print_info("PROD: Add Price to DB.\n", GREEN)
                        self.price_db.add_price(pr)
                    else:
                        print_info("PROD: ABANDON Prices!\n", RED)

            if self.prod:
                msg = "PROD: COMMIT Price DB edits and Save session."
                print_info("PROD: COMMIT Price DB edits and Save session.", GREEN)
                self.price_db.commit_edit()
                # only ONE session save for the entire run
                self.session.save()

            self.session.end()
            self.session.destroy()

        except Exception as e:
            msg = "get_prices_and_save() EXCEPTION!! '{}'".format(repr(e))
            print_error(msg)
            if "session" in locals() and self.session is not None:
                self.session.end()
                self.session.destroy()
            raise

        return msg
コード例 #31
0
def aa_sum_main():
    global gnucash_session
    exe = argv[0].split('/')[-1]
    if len(argv) < 9:
        print("NOT ENOUGH parameters!")
        print(
            "usage: {} <book url> <start year> <start month, numeric> <period type: 'monthly', 'quarterly', etc>"
            .format(exe))
        print("\t\t\t <number of periods to show, from start year and month>")
        print(
            "\t\t\t <whether to show debits: debits-show for true, all other values false>"
        )
        print(
            "\t\t\t <whether to show credits: credits-show for true, all other values false>"
        )
        print(
            "\t\t\t <space-separated account path, as many nested levels as desired>"
        )
        print("examples:\n")
        print(
            "The following example analyzes 12 months of Assets:TestAccount from <...>/test.gnucash, starting in January of 2018, and shows both credits and debits:"
        )
        print(
            "{} <...>/test.gnucash 2018 1 monthly 12 debits-show credits-show Assets TestAccount\n"
            .format(exe))
        print(
            "The following example analyzes 2 quarters of Liabilities:FirstLevel:SecondLevel from <...>/test.gnucash, starting March 2016, and shows credits but not debits:"
        )
        print(
            "{} <...>/test.gnucash 2016 3 quarterly 2 debx credits-show Liabilities <FirstLevel> <SecondLevel>"
            .format(exe))
        print("PROGRAM EXIT!")
        return

    try:
        (gnucash_file, start_year, start_month, period_type, periods,
         debits_show, credits_show) = argv[1:8]
        # mhs | debug
        print("showing {} periods of {} starting from {}-{}".format(
            periods, period_type, start_year, start_month))

        start_year, start_month, periods = [
            int(blah) for blah in (start_year, start_month, periods)
        ]

        # mhs | debug
        print("run-time: {}".format(str(datetime.now())))
        print("running: {}".format(exe))
        print("using gnucash file: {}".format(gnucash_file))

        debits_show = debits_show == DEBITS_SHOW
        credits_show = credits_show == CREDITS_SHOW

        account_path = argv[8:]
        # mhs | debug
        print("account_path = {}".format(str(account_path)))

        gnucash_session = Session(gnucash_file, is_new=False)

        root_account = gnucash_session.book.get_root_account()
        # mhs | debug
        # print( "root_account = " + root_account.GetName() )

        account_of_interest = account_from_path(root_account, account_path)
        # mhs | debug
        print("account_of_interest = {}".format(account_of_interest.GetName()))

        # 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
                ZERO  # TOTAL
            ] 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]

        # mhs | get the list of all descendant accounts
        descendants = account_of_interest.get_descendants()

        if len(descendants) == 0:
            # mhs | account has no descendants so just calculate the splits directly
            get_splits(account_of_interest, period_starts, period_list)
        else:
            # mhs | calculate the sums of debits and credits for EACH sub-account but just keep the overall total
            print("Descendants of {}:".format(account_of_interest.GetName()))
            for subAcct in descendants:
                print("{} balance = {}".format(subAcct.GetName(),
                                               subAcct.GetBalance()))
                get_splits(subAcct, period_starts, period_list)

        # write out the column headers
        csv_writer = csv.writer(stdout)
        csv_writer.writerow(())
        csv_writer.writerow(
            ('period start', 'period end', 'debits', 'credits', 'TOTAL'))

        def generate_detail_rows(values):
            return (('', '', '', '', trans.GetDescription(),
                     gnc_numeric_to_python_decimal(split.GetAmount()))
                    for trans, split in values)

        # write out the overall totals for the account of interest
        for start_date, end_date, debits, creds, debit_sum, credit_sum, total in period_list:
            csv_writer.writerow(
                (start_date, end_date, debit_sum, credit_sum, total))

            # write the details for each credit or debit if requested on the command line
            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(creds) > 0:
                csv_writer.writerow(
                    ('CREDITS', '', '', '', 'description', 'value'))
                csv_writer.writerows(generate_detail_rows(creds))
                csv_writer.writerow(())

        # no save needed, we're just reading..
        gnucash_session.end()
    except Exception as ae:
        if "gnucash_session" in locals() and gnucash_session is not None:
            gnucash_session.end()
        raise

    print("\n >>> PROGRAM ENDED.")
コード例 #32
0
def main():
	if len(argv) < 4:
		print 'Usage: python ofxml_make_commodity_accounts.py <gnucash-file> <ofxml-file> <accountmap-file>'
		exit(1)
		
	gnucash_file=argv[1]
	
	doc=ElementTree(file=argv[2])
	
	acctids=doc.findall('./INVSTMTMSGSRSV1/INVSTMTTRNRS/INVSTMTRS/INVACCTFROM/ACCTID')
	if len(acctids)!=1:
		print 'ERROR: No unique account number found in OFX: found', len(acctids)
		return
	acctid=acctids[0].text.strip()
	acctcur='any'
	m=re.search('^(.*)-([A-Z]{3})$',acctid)
	if m:
		acctid=m.group(1)
		acctcur=m.group(2)
	print "INFO: Account number:", acctid, "Currency:", acctcur

	fitids={}
	for itran in doc.findall('.//INVBUY')+doc.findall('.//INVSELL')+doc.findall('.//REINVEST'):
		fitid=itran.find('./INVTRAN/FITID')
		if not fitid is None:
			fitid=fitid.text.strip()
		if fitid in fitids:
			print "ERROR: Non-unique FITID found:", fitid
			exit(1)
		fitids[fitid]=itran
		
	# Fantastic, the FITID is not saved by GnuCash for income transactions...
	# Index by (date,amount,memo) instead
	incometrns={}
	for itran in doc.findall('.//INCOME'):
		fields={}
		for path in ('INVTRAN/DTTRADE', 'INVTRAN/MEMO', 'TOTAL'):
			el=itran.find('./'+path)
			if not el is None and len(el.text.strip())>0:
				fields[path]=el.text.strip()
		if len(fields)!=3:
			print "ERROR: Can't create identifier for INCOME transaction, ignoring."
		incometrns[(fields['INVTRAN/DTTRADE'][0:8],fields['INVTRAN/MEMO'],Fraction(fields['TOTAL']))]=itran

	sess=Session(gnucash_file)
	
	try:
		# Find GNC parent account
		root=sess.book.get_root_account()
		matched_accts=find_acct_by_number_and_currency(root,acctid,acctcur)
		if len(matched_accts)==0:
			from_iban=conv_iban(acctid)
			if from_iban:
				matched_accts=find_acct_by_number_and_currency(root,from_iban,acctcur)
			
		if len(matched_accts)!=1:
			print 'ERROR: No unique account this number/currency; found', len(matched_accts)
			return
		
		acct=matched_accts[0]
		print 'DEBUG: Found parent account:',acct.GetName()
		
		accountmap=read_accountmap(argv[3],acct.GetCode()+'-'+acct.GetCommodity().get_mnemonic())

		# Find child Stock/Mutual accounts
		secaccts=[] # SwigPyObject is not Hashable :(
		for cacct in acct.get_descendants():
			atype=cacct.GetType()
			if atype==gnucash_core.ACCT_TYPE_STOCK or atype==gnucash_core.ACCT_TYPE_MUTUAL:
				secaccts.append(cacct.get_instance())
				
		# Find income accounts
		incaccts=[] # SwigPyObject is not Hashable :(
		for typ in accountmap:
			if typ[0:6]=="INCOME":
				inst=find_acct_by_path(root,accountmap[typ]).get_instance()
				if not (inst is None or inst in incaccts):
					incaccts.append(inst)

		if len(incaccts)==0 and len(incometrns)>0:
			print 'WARNING: no income accounts defined for account',acct.GetCode()+'-'+acct.GetCommodity().get_mnemonic()
			print 'WARNING: income transactions will not be fixed'

		# Go through all transactions
		for tran in _py_xaccSplitListGetUniqueTransactions(acct.GetSplitList()):
			# Consider fixing if transaction ...
			# ... has exactly 2 splits
			# ... has 1 split with a child Stock/Mutual account
			# ... has 1 split with an online ID
			splits=tran.GetSplitList()
			if len(splits)==2:
				cashsplit=None
				secsplit=None
				incsplit=None
				online_id=None
				for split in splits:
					if split.GetAccount().get_instance() in secaccts:
						secsplit=split
					if split.GetAccount().get_instance() in incaccts:
						incsplit=split
					if split.GetAccount().get_instance()==acct.get_instance():
						cashsplit=split
					oid=_py_gnc_import_get_split_online_id(sess,split)
					if not oid is None:
						if online_id is None:
							online_id=oid
						else:
							online_id=False
				if not (cashsplit is None or secsplit is None or online_id is None or online_id is False):
					if not online_id in fitids:
						# This can happen if we encounter a transaction outside of this OFX period
						#print 'DEBUG: FITID',online_id,'not found in OFX file.'
						continue
					fix_buysell_transaction(tran,secsplit,cashsplit,fitids[online_id],root,accountmap)
				elif not (cashsplit is None or incsplit is None):
					date=tran.RetDatePostedTS().strftime('%Y%m%d')
					memo=re.sub(' +',' ',tran.GetDescription()) # GnuCash importer likes to insert spaces randomly
					amt=gnc_numeric_to_fraction(cashsplit.GetAmount())
					if not (date,memo,amt) in incometrns:
						# This can happen if we encounter a transaction outside of this OFX period
						#print "DEBUG: No match for income transaction",date,memo,amt
						continue
					fix_income_transaction(tran,incsplit,cashsplit,incometrns[(date,memo,amt)],root,accountmap)

		wait_for_backup_file(gnucash_file)
		sess.save()
	except BaseException as e:
		print 'ERROR:',e
	finally:    
		sess.end()
		sess.destroy()
コード例 #33
0
ファイル: invoice_pdf.py プロジェクト: bstpierre/gnucash-util
def invoice_pdf(gnc_file, invoice_number, pdf_file):
    uri = "xml://{0}".format(os.path.abspath(gnc_file))
    ses = Session(uri, is_new=False)
    try:
        book = ses.get_book()
        commod_table = book.get_table()
        USD = commod_table.lookup("CURRENCY", "USD")

        invoice = book.InvoiceLookupByID(invoice_number)
        client = invoice.GetOwner()
        client_addr = client.GetAddr()

        pdf = Canvas(pdf_file, bottomup=False, pagesize=letter)

        pdf.setFont("Helvetica-Bold", 24)
        pdf.setFillColor(colors.lightgrey)
        pdf.drawCentredString(letter[0] / 2, inch * 0.75, "INVOICE")

        font_height = 10
        pdf.setFont("Helvetica", font_height)
        pdf.setFillColor(colors.black)
        from_header = pdf.beginText(inch * 0.75, inch * 0.75)
        to_header = pdf.beginText(inch * 0.75, inch * 2.25)

        header_file = "{0}/.gnc-invoice-header".format(os.environ["HOME"])
        with open(header_file, "r") as f:
            for line in f:
                from_header.textLine(line.strip())

        to_fields = [
            client.GetName(),
            client_addr.GetName(),
            client_addr.GetAddr1(),
            client_addr.GetAddr2(),
            client_addr.GetAddr3(),
            client_addr.GetAddr4(),
        ]
        for field in to_fields:
            if field:
                to_header.textLine(field)

        pdf.drawText(from_header)
        pdf.drawText(to_header)

        #
        # This is the summary table / box in the mid-upper right.
        #
        table_data = (
            ("Invoice #", invoice.GetID()),
            ("Date", invoice.GetDatePosted().strftime("%Y-%m-%d")),
            ("Amount Due (USD)", "${0:0.2f}".format(invoice.GetTotal().to_double())),
        )
        x = inch * 4.5
        y = (inch * 2.25) - font_height
        width = inch * 3
        height = inch * 0.75
        num_rows = 3
        num_cols = 2
        col_width = width / num_cols
        row_height = height / num_rows
        for row in range(num_rows):
            for col in range(num_cols):
                rect_x = x + (col_width * col)
                rect_y = y + (row_height * row)

                pdf.setFillColor(colors.darkgrey)
                pdf.rect(rect_x, rect_y, col_width, row_height, stroke=True, fill=(col == 0))

                pdf.setFillColor(colors.black)
                if col:
                    pdf.drawAlignedString(rect_x + col_width, rect_y + font_height + 2, table_data[row][col], "%")
                else:
                    pdf.drawString(rect_x + 5, rect_y + font_height + 2, table_data[row][col])

        #
        # This is the detail table in the lower half.
        #
        table_data = [("Date", "Description", "Hours", "Rate ($)", "Line Total")]
        for entry in [Entry(instance=e) for e in invoice.GetEntries()]:
            qty = GncNumeric(instance=entry.GetQuantity()).to_double()
            rate = GncNumeric(instance=entry.GetInvPrice()).to_double()
            line_total = GncNumeric(instance=entry.ReturnValue(True)).to_double()
            row = [
                entry.GetDate().strftime("%Y-%m-%d"),
                entry.GetDescription(),
                "{0:0.2f}".format(qty),
                "{0:0.2f}".format(rate),
                "{0:0.2f}".format(line_total),
            ]
            table_data.append(row)

        x = inch * 0.75
        y = inch * 4.0

        # Let column 1 consume the rest of the space.
        width = inch * 6.75
        widths = [80, 0, 50, 50, 80]
        widths[1] = width - sum(widths)

        height = font_height + 2 + 2  # 2pt spacing above and below.
        num_rows = 1
        num_cols = 5
        col_width = width / num_cols
        row_height = height / num_rows
        rect_x = x
        rect_y = y
        for row_num, row in enumerate(table_data):
            rect_x = x
            for col_num, col in enumerate(row):
                col_width = widths[col_num]
                rect_y = y + (row_height * row_num)

                pdf.setFillColor(colors.darkgrey)
                pdf.rect(rect_x, rect_y, col_width, row_height, stroke=True, fill=(row_num == 0))

                pdf.setFillColor(colors.black)
                if col_num > 1:
                    pdf.drawAlignedString(rect_x + col_width, rect_y + font_height + 2, col, "%")
                else:
                    pdf.drawString(rect_x + 5, rect_y + font_height + 2, col)

                rect_x = rect_x + col_width

        # Draw the outer detail box.
        detail_height = inch * 5.0
        pdf.setFillColor(colors.black)
        pdf.rect(x, y, width, detail_height, stroke=True, fill=False)

        # Total box above payment terms.
        totalbox_text_vpad = 6
        totalbox_text_height = font_height + (totalbox_text_vpad * 2)
        totalbox_rows = 4
        totalbox_height = totalbox_text_height * (totalbox_rows + 1)
        totalbox_y = y + detail_height - totalbox_height
        pdf.rect(x, totalbox_y, width, totalbox_height, stroke=True, fill=False)

        # Total, balance due, etc boxes inside total box.
        total_amount = invoice.GetTotal().to_double()
        totalbox_data = [
            ("Subtotal:", "${0:0.2f}".format(total_amount)),
            ("Total:", "${0:0.2f}".format(total_amount)),
            ("Amount Paid:", "$0.00"),
            ("Balance Due:", "${0:0.2f}".format(total_amount)),
        ]
        balance_height = row_height
        balance_y = totalbox_y + totalbox_height - totalbox_text_height * 2
        for n in xrange(totalbox_rows):
            thisbox_y = totalbox_y + totalbox_text_height * n
            thisbox_text_y = thisbox_y + totalbox_text_height - totalbox_text_vpad
            pdf.setFillColor(colors.lightgrey)
            pdf.rect(
                x + width / 2, thisbox_y, width / 2, totalbox_text_height, stroke=True, fill=(n == (totalbox_rows - 1))
            )
            pdf.setFillColor(colors.black)
            pdf.drawAlignedString(x + width / 2 + (inch * 1.5), thisbox_text_y, totalbox_data[n][0], ":")
            pdf.drawAlignedString(x + width - 20, thisbox_text_y, totalbox_data[n][1], ".")

        # Payment terms in the bottom of the detail box.
        pdf.setFillColor(colors.black)
        pdf.rect(x, y + detail_height - totalbox_text_height, width, totalbox_text_height, stroke=True, fill=False)

        if invoice.GetTerms() and invoice.GetTerms().GetDueDays():
            due = "Payment due within %d days of invoice date." % (invoice.GetTerms().GetDueDays())
            pdf.setFillColor(colors.black)
            pdf.drawCentredString(x + (width / 2), y + detail_height - totalbox_text_vpad, due)

        pdf.showPage()
        pdf.save()
    finally:
        ses.end()
コード例 #34
0
def main():
    args = parse_cmdline()
    if args.version:
        print(VERSION)
        exit(0)

    if args.verbose:
        loglevel = logging.DEBUG
    elif args.quiet:
        loglevel = logging.WARN
    else:
        loglevel = logging.INFO
    logging.basicConfig(level=loglevel)

    rules = readrules(args.rulesfile)
    account_path = re.split(':', args.ac2fix)

    gnucash_session = Session(args.gnucash_file, is_new=False)
    total = 0
    imbalance = 0
    fixed = 0
    confirmed = None
    try:
        root_account = gnucash_session.book.get_root_account()
        orig_account = account_from_path(root_account, account_path)

        imbalance_pattern = re.compile(args.imbalance_ac)

        for split in orig_account.GetSplitList():
            total += 1
            trans = split.parent
            splits = trans.GetSplitList()
            trans_date = trans.GetDate().date()
            trans_desc = trans.GetDescription()
            trans_memo = trans.GetNotes()
            for split in splits:
                ac = split.GetAccount()
                acname = ac.GetName()
                amount = eval(str(split.GetAmount()))
                # "Debit balance accounts" are represented with a negative sign
                # internally, so the amounts need their signs flipped.
                # Refer to section 2.1.3 Debits and Credits in the GnuCash docs,
                # the rearranged accounting equation to see why.
                if account_path[0] in ('Assets', 'Expenses'):
                    amount = -amount
                logging.debug('%s: %s => %s', trans_date, trans_desc, acname)
                if imbalance_pattern.match(acname):
                    imbalance += 1
                    search_str = trans_desc
                    if args.use_memo:
                        search_str = trans_memo
                    newac = get_ac_from_str(search_str, rules, root_account)
                    if newac != "":
                        if args.confirm:
                            print(
                                bold + green + '\nFound a match!\n' + end +
                                '{}: "{}" = {:.2f}\nFix account "{}" to "{}"?'.
                                format(trans_date, cyan + trans_desc +
                                       end, amount, acname, cyan +
                                       newac.GetName() + end))
                            if confirmed != 'all':
                                confirmed = input(
                                    "Press ENTER to fix, "
                                    "'all' to fix all remaining, "
                                    "'s' to skip, 'q' to quit: ").lower(
                                    ).strip()
                            if confirmed == 'q':
                                print("Quitting...")
                                gnucash_session.end()
                                sys.exit()
                            elif confirmed not in ('', 'all'):
                                # If not ENTER, 'y', or 'all', then skip!
                                print("Skipping...")
                                continue
                            print("Fixing...")
                        logging.debug('\tChanging account to: %s',
                                      newac.GetName())
                        split.SetAccount(newac)
                        fixed += 1

        if not args.nochange:
            gnucash_session.save()

        logging.info('Total splits=%s, imbalance=%s, fixed=%s', total,
                     imbalance, fixed)

    except Exception as ex:
        logging.error(ex)

    gnucash_session.end()
コード例 #35
0
def main():

    if len(argv) < 3:
        print('not enough parameters')
        print('usage: new_book_with_opening_balances.py {source_book_url} {destination_book_url}')
        print('examples:')
        print("gnucash-env python new_book_with_opening_balances.py '/home/username/test.gnucash' 'sqlite3:///home/username/new_test.gnucash'")
        print("gnucash-env python new_book_with_opening_balances.py '/home/username/test.gnucash' 'xml:///crypthome/username/finances/new_test.gnucash'")
        return

    #have everything in a try block to unable us to release our hold on stuff to the extent possible
    try:
        original_book_session = Session(argv[1], is_new=False)
        new_book_session = Session(argv[2], is_new=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()
    except:
        if "original_book_session" in locals():
            original_book_session.end()
 
        if "new_book_session" in locals():
            new_book_session.end()

        raise
コード例 #36
0
def map_customers(request):
    rows = request.session.get("rows", [])
    map = request.session.get("map", {})

    data = []
    for row in rows:
        new_row = {}
        for index, field in map.items():
            new_row[field] = row[int(index)]
        if not new_row["amount"].startswith("-"):
            data.append(new_row)

    session = Session(settings.GNUCASH_FILE)

    CustomerFormSet = formset_factory(CustomerForm, extra=0)

    if request.method == "POST":
        formset = CustomerFormSet(request.POST,
                                  form_kwargs={"book": session.book})
        try:
            ok = dup = 0
            for form in formset.forms:
                if form.is_valid():
                    clean = form.cleaned_data
                    try:
                        commands.apply_payment(
                            session.book,
                            clean["customer"],
                            clean["amount"],
                            clean["date"],
                        )
                        ok += 1
                    except PaymentExists as e:
                        messages.warning(request, e)
                        dup += 1

            session.save()
            if ok:
                messages.info(request,
                              "Successfully imported %s transactions" % ok)
            if dup:
                messages.warning(request,
                                 "Skipped %s duplicate transactions" % dup)

        except Exception as e:
            # messages.error(request, e)
            raise

        finally:
            session.end()

        return HttpResponseRedirect(reverse("income-finish"))
    else:
        initial = []
        for row in data:
            customer = queries.match_customer(session.book,
                                              row.get("customer"))
            initial.append({
                "customer": customer,
                "amount": row.get("amount"),
                "date": row.get("date"),
                "description": row.get("customer"),
            })
        formset = CustomerFormSet(initial=initial,
                                  form_kwargs={"book": session.book})

    session.end()

    return render(
        request,
        "importer/map-customers.html",
        {
            "formset": formset,
            "rows": zip(data, formset.forms),
            "gnucash": settings.GNUCASH_FILE,
        },
    )
コード例 #37
0
ファイル: expenses.py プロジェクト: drjeep/gnucash-importer
def map_accounts(request):
    rows = request.session.get("rows", [])
    map = request.session.get("map", {})
    statement = request.session.get("statement", "bank")
    if statement == "bank":
        bank_account = settings.GNUCASH_BANK_ACCOUNT
    else:
        bank_account = settings.GNUCASH_CARD_ACCOUNT

    data = []
    for row in rows:
        new_row = {}
        for index, field in map.items():
            new_row[field] = row[int(index)]
        # @todo: split into debit/credit views
        if statement == "card" or (new_row["amount"].startswith("-")
                                   or "VIRTUALSTOCK" in new_row["account"]):
            data.append(new_row)

    AccountFormSet = formset_factory(AccountForm, extra=0)

    if request.method == "POST":
        session = Session(settings.GNUCASH_FILE)
        formset = AccountFormSet(request.POST,
                                 form_kwargs={'book': session.book})

        root = session.book.get_root_account()
        if statement == "bank":
            bank = root.lookup_by_name(settings.GNUCASH_BANK_ACCOUNT)
        else:
            bank = root.lookup_by_name(settings.GNUCASH_CARD_ACCOUNT)

        check = queries.get_duplicate_check_data(bank)
        log.debug(check)

        try:
            ok = dup = 0
            for form in formset.forms:
                if form.is_valid():
                    clean = form.cleaned_data
                    if [clean["date"], clean["amount"]] not in check:
                        create_split_transaction(
                            session.book,
                            bank_account,
                            str(clean["account"]),
                            clean["date"],
                            str(clean["description"]),
                            clean["amount"],
                            vat_incl=clean["vat_incl"],
                        )
                        ok += 1
                    else:
                        log.debug(
                            "Skipped %s %s %s" % (
                                clean["date"].strftime("%Y-%m-%d"),
                                clean["description"],
                                clean["amount"],
                            ), )
                        dup += 1

            session.save()
            messages.info(request,
                          "Successfully imported %s transactions" % ok)
            if dup:
                messages.warning(request,
                                 "Skipped %s duplicate transactions" % dup)

        except Exception as e:
            messages.error(request, e)

        finally:
            session.end()

        return HttpResponseRedirect(reverse("expenses-finish"))
    else:
        initial = []
        for row in data:
            account, vat_incl = queries.match_account(row.get("account"),
                                                      row.get("amount", 0))
            initial.append({
                "account": account,
                "amount": row.get("amount"),
                "date": row.get("date"),
                "description": row.get("account"),
                "vat_incl": vat_incl,
            })
        formset = AccountFormSet(initial=initial)

    return render(
        request,
        "importer/map-accounts.html",
        {
            "formset": formset,
            "rows": zip(data, formset.forms),
            "gnucash": settings.GNUCASH_FILE,
        },
    )
コード例 #38
0
class Gnucash:
    __seendict = {}

    def __init__(self, book_uri):
        self._session = Session(book_uri=book_uri)
        self._book = self._session.book
        self._root_account = self._book.get_root_account()

        self._commodity_table = self._book.get_table()

        # todo: implement as class w/ getattr
        self.commods = {}
        self.commods['USD'] = self._commodity_table.lookup("ISO4217", "USD")

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        self._session.end()

    def save(self):
        self._session.save()

    def seen(self, acct, num):
        if repr(acct) not in self.__seendict:
            l = []
            for split in acct.GetSplitList():
                txn = split.parent
                if txn.GetNum() not in l:
                    l.append(txn.GetNum().strip())
            self.__seendict[repr(acct)] = l

        return num in self.__seendict[repr(acct)]

    def _account_from_path(self, 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 == None or account.get_instance() == None:
            raise Exception(
                "path " + ''.join(original_path) + " could not be found")
        if len(account_path) > 0 :
            return self._account_from_path(account, account_path, original_path)
        else:
            return account

    def account(self, account_path):
        return self._account_from_path(self._root_account, account_path.split(':'))

    def rat(self, value):
        s = int(round(value*100))
        return GncNumeric(s, 100)

    def NewTransaction(self):
        t = Transaction(self._book)
        t.BeginEdit()
        t.SetCurrency(self.commods['USD'])
        return t

    def NewSplit(self, txn, acct, amount):
        s = Split(self._book)
        s.SetParent(txn)
        s.SetAccount(acct)
        s.SetAmount(self.rat(amount))
        s.SetValue(self.rat(amount))
        return s

    def TransactionReadyToCommit(self, txn):
        for split in txn.GetSplitList():
            if split.GetValue().to_double() == 0.0:
                split.Destroy()

        return txn.IsBalanced()

    def InvoiceLookupByID(self, id):
        return self._book.InvoiceLookupByID(id)

    def PayInvoiceWithTransaction(self, invoice, txn, acct, gross, memo, num):
        invoice.BeginEdit()
        invoice.ApplyPayment(txn, acct, self.rat(-gross), self.rat(1), txn.RetDatePostedTS(), memo, num)
        invoice.CommitEdit()

    def ApplyPaymentToCustomer(self, customer, txn, post_acct, from_acct, gross, memo, num):
        print("Applying payment of %s to customer %s" % (gross, customer.GetName()))
        customer.BeginEdit()
        customer.ApplyPayment(txn, None, post_acct, from_acct, self.rat(-gross), self.rat(1), txn.RetDatePostedTS(), memo, num, True)
        customer.CommitEdit()

    def GetCustomerByEmail(self, email):
        q = Query()
        q.search_for('gncCustomer')
        q.set_book(self._book)

        c = None

        for result in q.run():
            tmp = Customer(instance=result)
            if tmp.GetAddr().GetEmail().lower() == email.lower():
                c = tmp
                break

        q.destroy()

        return c

    def GetCustomerByName(self, name):
        q = Query()
        q.search_for('gncCustomer')
        q.set_book(self._book)

        c = None

        for result in q.run():
            tmp = Customer(instance=result)
            if tmp.GetName().lower() in name.lower():
                c = tmp
                break

        q.destroy()

        return c
コード例 #39
0
class GnuCashHelper(object):
    """some hany functions around gnucash api"""
    def __init__(self, filename):
        self.__session = Session(filename, is_new=False)
        self.__book = self.__session.book

    def account_from_path(self, top_account, account_path, original_path=None):
        """
        return gnucash account object from account path in this notation

        <Account>.<SubAccount>.<SubAccount>

        Accounts are splitted by dots
        """
        if original_path is None:
            original_path = account_path
        account, account_path = account_path[0], account_path[1:]
        account = top_account.lookup_by_name(account)
        #print(account.name)
        if account.get_instance() is None:
            raise Exception("path " + ''.join(original_path) +
                            " could not be found")
        if len(account_path) > 0:
            return self.account_from_path(account, account_path, original_path)
        return account

    def lookup_account(self, root, name):
        """
        find account by name
        """
        path = name.split('.')
        return self.lookup_account_by_path(root, path)

    def lookup_account_by_path(self, root, path):
        """
        return account given path
        """
        acc = root.lookup_by_name(path[0])
        if acc.get_instance() is None:
            raise Exception('Account path {} not found'.format('.'.join(path)))
        if len(path) > 1:
            return self.lookup_account_by_path(acc, path[1:])
        return acc

    def get_root(self):
        """
        return root account
        """
        return self.__book.get_root_account()

    @staticmethod
    def tokenizer(entry, blacklist_tokens=None):
        """
        cut string into tokens
        entry has to be in gnucach entry data format
        """
        # like {'haben': 'Ausgleichskonto-EUR', 'soll':
        # 'Aktiva.PSK-Konto', 'num': '', 'description': 'Abgleich',
        # 'date': '2000-01-01', 'haben_value': '3002.63', 'notes':
        # 'None', 'soll_value': '-3002.63'}
        if blacklist_tokens is None:
            blacklist_tokens = []
        tokens = re.findall(r'(?ms)\W*(\w+)', entry["description"])
        tokens.extend(re.findall(r'(?ms)\W*(\w+)', entry["notes"]))
        tokens.extend(entry["description"].split(" "))
        tokens.extend(entry["notes"].split(" "))
        final_tokens = []
        for token in tokens:
            if len(token) <= 2:
                continue
            if token not in final_tokens:
                final_tokens.append(token.lower())
            # find some subtokens from  og/123762137
            for subtoken in token.split("/"):
                if subtoken not in final_tokens:
                    final_tokens.append(subtoken)
        if entry["soll_value"] > 0.0:
            final_tokens.append("einnahmen")
        else:
            final_tokens.append("ausgaben")
        return entry["haben"], [
            token for token in final_tokens if token not in blacklist_tokens
        ]

    def gnucash_export(self, path):
        """
        export all splits of particular account given by path
        path should be joined by .
        """
        account_path = path.split(".")
        try:
            root_account = self.get_root()
            print(root_account.name)
            orig_account = self.account_from_path(root_account, account_path)
            print(orig_account.name)
            print(orig_account.get_full_name())
            for split in orig_account.GetSplitList():
                other = split.GetOtherSplit()
                if other is None:
                    print("there is not other account")
                    sys.exit(2)
                other_account = other.GetAccount()
                if other_account is None:
                    print("No other account found")
                    sys.exit(1)
                other_name = other_account.get_full_name()
                other_value = other.GetValue()
                # 01.02.2012;Internet;;36336 0000123445 Walter oder Monika
                # ;Internet FE/000004174;;PSK-Konto;T;;N;15,50 ;;15,50;;;
                # get transaction to witch split belongs
                # a transaction consists of 1..n splits
                trans = split.parent
                if len(trans.GetSplitList()) > 2:
                    print(
                        "there are more than 2 accounts involved, not supported"
                    )
                    sys.exit(3)
                csv_line = {
                    "soll": orig_account.get_full_name(),
                    "haben": other_name,
                    "date": str(datetime.date.fromtimestamp(trans.GetDate())),
                    "description": str(trans.GetDescription()),
                    "notes": str(trans.GetNotes()),
                    "num": str(trans.GetNum()),
                    "soll_value": str(split.GetValue()),
                    "haben_value": str(other_value),
                }
                yield csv_line
                #print(json.dumps(csv_line, indent=4))
                #for tsplit in trans.GetSplitList():
                #    account = tsplit.GetAccount()
                #    account_name = str(account.GetName())
                #    value = str(tsplit.GetValue())
        except Exception as exc:
            logging.exception(exc)

    def get_latest_booking(self, path):
        """
        return latest booking of this particular account
        """
        account_path = path.split(".")
        try:
            root_account = self.get_root()
            print(root_account.name)
            orig_account = self.account_from_path(root_account, account_path)
            print(orig_account.name)
            print(orig_account.get_full_name())
            split = orig_account.GetSplitList()[-1]
            other = split.GetOtherSplit()
            if other is None:
                print("there is not other account")
                sys.exit(2)
            other_account = other.GetAccount()
            if other_account is None:
                print("No other account found")
                sys.exit(1)
            other_name = other_account.get_full_name()
            other_value = other.GetValue()
            # 01.02.2012;Internet;;36336 00007712341 Walter oder Monika
            # ;Internet FE/000004174;;PSK-Konto;T;;N;15,50 ;;15,50;;;
            # get transaction to witch split belongs
            # a transaction consists of 1..n splits
            trans = split.parent
            if len(trans.GetSplitList()) > 2:
                print("there are more than 2 accounts involved, not supported")
                sys.exit(3)
            csv_line = {
                "soll": orig_account.get_full_name(),
                "haben": other_name,
                "date": str(datetime.date.fromtimestamp(trans.GetDate())),
                "description": str(trans.GetDescription()),
                "notes": str(trans.GetNotes()),
                "num": str(trans.GetNum()),
                "soll_value": str(split.GetValue()),
                "haben_value": str(other_value),
            }
            return csv_line
        except Exception as exc:
            logging.exception(exc)

    def add_transaction(self, item):
        """
        add new transaction
        item must have following keys
        """
        assert "date" in item.keys()
        assert "description" in item.keys()
        assert "notes" in item.keys()
        assert "soll" in item.keys()
        assert "soll_value" in item.keys()
        assert "haben" in item.keys()
        assert "haben_value" in item.keys()
        commod_tab = self.__book.get_table()
        currency = commod_tab.lookup('ISO4217', "EUR")
        logging.info('Adding transaction for account "%s" (%s %s)..',
                     item["soll"], item["soll_value"], currency.get_mnemonic())
        tx = Transaction(self.__book)
        tx.BeginEdit()
        tx.SetCurrency(currency)
        tx.SetDateEnteredTS(datetime.datetime.now())
        tx.SetDatePostedTS(item["date"])
        tx.SetDescription(item["description"])
        tx.SetNotes(item["notes"])
        if "num" in item.keys():
            tx.SetNum(item["num"])
        # soll
        acc = self.account_from_path(self.get_root(), item["soll"].split("."))
        s1 = Split(self.__book)
        s1.SetParent(tx)
        s1.SetAccount(acc)
        amount = int(item["soll_value"] * currency.get_fraction())
        s1.SetValue(GncNumeric(amount, currency.get_fraction()))
        s1.SetAmount(GncNumeric(amount, currency.get_fraction()))
        # haben
        acc2 = self.account_from_path(self.get_root(),
                                      item["haben"].split("."))
        s2 = Split(self.__book)
        s2.SetParent(tx)
        s2.SetAccount(acc2)
        amount = int(item["haben_value"] * currency.get_fraction())
        s2.SetValue(GncNumeric(amount, currency.get_fraction()))
        s2.SetAmount(GncNumeric(amount, currency.get_fraction()))
        tx.CommitEdit()

    def end(self):
        """
        should be always called to prevent locking
        TODO: maybe there is some better way to implement this
        """
        self.__session.end()
コード例 #40
0
ファイル: change_tax_code.py プロジェクト: mchochlov/Gnucash
# choose the account code to select
TARGET_ACCOUNT_CODE = '1234'


def mark_account_with_code_as_tax_related(account, target_code):
    """Looks at account to see if it has the target_account_code, if so
    set the account tax related flag to True and return True.
    If not, recursively tries to do the same to all children accounts
    of account.
    Returns False when recursion fails to find it.
    """
    if account.GetCode() == target_code:
        account.SetTaxRelated(True)
        return True
    else:
        for child in account.get_children():
            child = Account(instance=child)
            if mark_account_with_code_as_tax_related(child, target_code):
                return True
        return False


# Change this path to your own
gnucash_session = Session("/home/mark/python-bindings-help/test.xac")

mark_account_with_code_as_tax_related(gnucash_session.book.get_root_account(),
                                      TARGET_ACCOUNT_CODE)

gnucash_session.save()
gnucash_session.end()
コード例 #41
0
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(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():
        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()
コード例 #42
0
def main():

    if len(argv) < 10:
        print('not enough parameters')
        print(
            'usage: account_analysis.py {book url} {start year} {start month, numeric} {period type: monthly, quarterly, or yearly} {number of periods to show, from start year and month} {whether to show debits: debits-show for true, all other values false} {whether to show credits: credits-show for true, all other values false} {space separated account path, as many nested levels as desired} '
        )
        print('examples:\n')
        print(
            "The following example analyzes 12 months of 'Assets:Test Account' from /home/username/test.gnucash, starting in January of 2010, and shows both credits and debits"
        )
        print(
            "gnucash-env python account_analysis.py '/home/username/test.gnucash' 2010 1 monthly 12 debits-show credits-show Assets 'Test Account'\n"
        )
        print(
            "The following example analyzes 2 quarters of 'Liabilities:First Level:Second Level' from /home/username/test.gnucash, starting March 2011, and shows credits but not debits"
        )
        print(
            "gnucash-env python account_analysis.py '/home/username/test.gnucash' 2011 3 quarterly 2 debits-noshow credits-show Liabilities 'First Level' 'Second Level"
        )
        return

    try:
        (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(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():
            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()
    except:
        if "gnucash_session" in locals():
            gnucash_session.end()

        raise
コード例 #43
0
class Gnucash:
    __seendict = {}

    def __init__(self, book_uri):
        self._session = Session(book_uri=book_uri)
        self._book = self._session.book
        self._root_account = self._book.get_root_account()

        self._commodity_table = self._book.get_table()

        # todo: implement as class w/ getattr
        self.commods = {}
        self.commods["USD"] = self._commodity_table.lookup("ISO4217", "USD")

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        self._session.end()

    def save(self):
        self._session.save()

    def seen(self, acct, num):
        if repr(acct) not in self.__seendict:
            l = []
            for split in acct.GetSplitList():
                txn = split.parent
                if txn.GetNum() not in l:
                    l.append(txn.GetNum().strip())
            self.__seendict[repr(acct)] = l

        return num in self.__seendict[repr(acct)]

    def _account_from_path(self, 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 self._account_from_path(account, account_path, original_path)
        else:
            return account

    def account(self, account_path):
        return self._account_from_path(self._root_account, account_path.split(":"))

    def rat(self, value):
        s = int(round(value * 100))
        return GncNumeric(s, 100)

    def NewTransaction(self):
        t = Transaction(self._book)
        t.BeginEdit()
        t.SetCurrency(self.commods["USD"])
        return t

    def NewSplit(self, txn, acct, amount):
        s = Split(self._book)
        s.SetParent(txn)
        s.SetAccount(acct)
        s.SetAmount(self.rat(amount))
        s.SetValue(self.rat(amount))
        return s

    def TransactionReadyToCommit(self, txn):
        for split in txn.GetSplitList():
            if split.GetValue().to_double() == 0.0:
                split.Destroy()

        return txn.IsBalanced()

    def InvoiceLookupByID(self, id):
        return self._book.InvoiceLookupByID(id)

    def PayInvoiceWithTransaction(self, invoice, txn, acct, gross, memo, num):
        invoice.BeginEdit()
        invoice.ApplyPayment(txn, acct, self.rat(-gross), self.rat(1), txn.RetDatePostedTS(), memo, num)
        invoice.CommitEdit()

    def ApplyPaymentToCustomer(self, customer, txn, post_acct, from_acct, gross, memo, num):
        customer.BeginEdit()
        customer.ApplyPayment(
            txn, None, post_acct, from_acct, self.rat(-gross), self.rat(1), txn.RetDatePostedTS(), memo, num, True
        )
        customer.CommitEdit()

    def GetCustomerByEmail(self, email):
        q = Query()
        q.search_for("gncCustomer")
        q.set_book(self._book)

        c = None

        for result in q.run():
            tmp = Customer(instance=result)
            if tmp.GetAddr().GetEmail().lower() == email.lower():
                c = tmp
                break

        q.destroy()

        return c
コード例 #44
0
#!/usr/bin/env python

##  @file
#   @brief Simple example for a book
#   @ingroup python_bindings_examples

import sys
from gnucash import Session

# We need to tell GnuCash the data format to create the new file as (xml://)
uri = "xml:///tmp/simple_book.gnucash"

print("uri:", uri)
ses = Session(uri, is_new=True)
book = ses.get_book()

#Call some methods that produce output to show that Book works
book.get_root_account().SetDescription("hello, book")
print("Book is saved:", not book.session_not_saved())

print("saving...")
ses.save()

print("Book is saved:", not book.session_not_saved())
ses.end()
コード例 #45
0
class TestQueries(TestCase):
    def setUp(self):
        self.session = Session(
            os.path.join(os.path.dirname(os.path.abspath(__file__)),
                         "test.gnucash"))

    @classmethod
    def setUpTestData(cls):
        AccountMap.objects.create(match="Telkom",
                                  account="Phone",
                                  vat_inclusive=True)
        AccountMap.objects.create(match="Vodacom",
                                  account="Cellphone",
                                  vat_inclusive=False)

    def tearDown(self):
        self.session.end()

    def test_get_accounts(self):
        self.assertEqual(
            len(queries.get_accounts(self.session.book.get_root_account())),
            62)

    def test_get_account_ancestors(self):
        root = self.session.book.get_root_account()
        acc = root.lookup_by_name(settings.GNUCASH_BANK_ACCOUNT)
        self.assertEqual(len(queries.get_account_ancestors(acc)), 3)

    def test_get_customers(self):
        customers = queries.get_customers(self.session.book)
        self.assertEqual(len(customers), 2)

    def test_get_invoices(self):
        self.assertEqual(len(queries.get_invoices(self.session.book)), 3)

    def test_get_invoices_customer(self):
        customer = self.session.book.CustomerLookupByID("000001")
        self.assertEqual(
            len(queries.get_invoices(self.session.book, customer)), 2)

    def test_get_account_maps(self):
        self.assertEqual(
            queries.get_account_maps(),
            [("Telkom", "Phone", True), ("Vodacom", "Cellphone", False)],
        )

    def test_match_account(self):
        self.assertEqual(queries.match_account("1 Telkom Ltd"),
                         ("Phone", True))

    def test_match_account_not_found(self):
        self.assertEqual(queries.match_account("not found"), (None, False))

    def test_match_customer_name(self):
        self.assertEqual(queries.match_customer(self.session.book, "Acme"),
                         "000001")

    def test_match_customer_invoice(self):
        self.assertEqual(queries.match_customer(self.session.book, "00002"),
                         "000001")

    def test_match_customer_not_found(self):
        self.assertEqual(
            queries.match_customer(self.session.book, "not found"), None)

    def test_get_payment_refs(self):
        self.assertEqual(queries.get_payment_refs(self.session.book),
                         {"000001"})

    def test_get_duplicate_check_data(self):
        root = self.session.book.get_root_account()
        acc = root.lookup_by_name(settings.GNUCASH_BANK_ACCOUNT)
        self.assertEqual(
            queries.get_duplicate_check_data(acc),
            [[date(2019, 11, 21), Decimal("9.99")]],
        )
コード例 #46
0
ファイル: simple_book.py プロジェクト: JohannesKlug/gnucash
#!/usr/bin/env python

##  @file
#   @brief Simple example for a book
#   @ingroup python_bindings_examples

import sys
from gnucash import Session

# We need to tell GnuCash the data format to create the new file as (xml://)
uri = "xml:///tmp/simple_book.gnucash"

print "uri:", uri
ses = Session(uri, is_new=True)
book = ses.get_book()

# Call some methods that produce output to show that Book works
book.get_root_account().SetDescription("hello, book")
print "Book is saved:", not book.not_saved()

print "saving..."
ses.save()

print "Book is saved:", not book.not_saved()
ses.end()
コード例 #47
0
def get_exps_qtr_main():
    global gnucash_session
    exe = argv[0].split('/')[-1]
    if len(argv) < 4:
        print("NOT ENOUGH parameters!")
        print("usage: {} <book url> <year> <quarter>".format(exe))
        print("PROGRAM EXIT!")
        return

    print("\nrunning {} at run-time: {}\n".format(exe, str(datetime.now())))

    try:
        (gnucash_file, str_year, str_quarter) = argv[1:4]
        print("find Expenses in {} for {}-Q{}".format(gnucash_file, str_year,
                                                      str_quarter))

        rev_year, rev_quarter = [int(blah) for blah in (str_year, str_quarter)]

        start_month = (rev_quarter * 3) - 2

        # a list of all the periods of interest
        # for each period keep the start date, end date, debits and credits sums and overall total
        period_list = [
            [
                start_date,
                end_date,
                ZERO,  # debits sum
                ZERO,  # credits sum
                ZERO  # TOTAL
            ] for start_date, end_date in generate_period_boundaries(
                rev_year, start_month, 'quarterly', 1)
        ]
        # print(period_list)
        # a copy of the above list with just the period start dates
        period_starts = [e[0] for e in period_list]
        # print(period_starts)

        gnucash_session = Session(gnucash_file, is_new=False)
        root_account = gnucash_session.book.get_root_account()

        for item in EXP_ACCTS:
            # reset the debit and credit totals for each individual account
            period_list[0][2] = 0
            period_list[0][3] = 0

            acct_base = EXP_ACCTS[item]
            # print("acct = {}".format(acct_base))

            account_of_interest = account_from_path(root_account, acct_base)
            acct_name = account_of_interest.GetName()
            print("\naccount_of_interest = {}".format(acct_name))

            # get the split amounts for the parent account
            get_splits(account_of_interest, period_starts, period_list)

            descendants = account_of_interest.get_descendants()
            if len(descendants) > 0:
                # for EACH sub-account add to the overall total
                # print("Descendants of {}:".format(account_of_interest.GetName()))
                for subAcct in descendants:
                    # print("{} balance = {}".format(subAcct.GetName(), gnc_numeric_to_python_decimal(subAcct.GetBalance())))
                    get_splits(subAcct, period_starts, period_list)

            # write out the column headers
            csv_writer = csv.writer(stdout)
            # csv_writer.writerow('')
            csv_writer.writerow(
                ('period start', 'period end', 'debits', 'credits', 'TOTAL'))

            # write out the overall totals for the account of interest
            for start_date, end_date, debit_sum, credit_sum, total in period_list:
                csv_writer.writerow(
                    (start_date, end_date, debit_sum, credit_sum, total))

            sum_expenses = (period_list[0][2] + period_list[0][3])
            print("{} Expenses for {}-Q{} = ${}".format(
                acct_name.split('_')[-1], str_year, str_quarter, sum_expenses))

        tot_expenses = period_list[0][4]
        print("\n{} Expenses for {}-Q{} = ${}".format("TOTAL", str_year,
                                                      str_quarter,
                                                      tot_expenses))

        # no save needed, we're just reading..
        gnucash_session.end()
    except Exception as qe:
        if "gnucash_session" in locals() and gnucash_session is not None:
            gnucash_session.end()
        raise

    print("\n >>> PROGRAM ENDED.")
コード例 #48
0
ファイル: quotes_historic.py プロジェクト: Mechtilde/gnucash
  print('Need at least one Split to get currency info ... ')
  raise SystemExit
cur = ac.GetSplitList()[0].GetParent().GetCurrency()

# Get stock data
pl = pdb.get_prices(stock,cur)
if len(pl)<1:
  print('Need at least one database entry to clone ...')
  raise SystemExit

pl0 = pl[0]
for i in range(1,len(pl)):
  pdb.remove_price(pl[i])

for i in range(0,len(stock_date)):
  p_new = pl0.clone(book)
  p_new = gnucash.GncPrice(instance=p_new)
  print('Adding',i,stock_date[i],stock_price[i])
  p_new.set_time(stock_date[i])
  v = p_new.get_value()
  v.num = int(Fraction.from_float(stock_price[i]).limit_denominator(100000).numerator)
  v.denom = int(Fraction.from_float(stock_price[i]).limit_denominator(100000).denominator)
  p_new.set_value(v)
  p_new.set_source("Finance::Quotes::Historic")
  pdb.add_price(p_new)

# Clean up
session.save()
session.end()
session.destroy()
コード例 #49
0
def main():
	if len(argv) < 3:
		print 'Usage: python ofxml_make_commodity_accounts.py <gnucash-file> <ofxml-file> [symbols-file]'
		exit(1)
		
	gnucash_file=argv[1]
	
	symbols_file=False
	if len(argv)>=4:
		symbols_file=read_symbols(argv[3])
	
	doc=ElementTree(file=argv[2])
	
	acctids=doc.findall('./INVSTMTMSGSRSV1/INVSTMTTRNRS/INVSTMTRS/INVACCTFROM/ACCTID')
	if len(acctids)!=1:
		print 'ERROR: No unique account number found in OFX: found', len(acctids)
		return
	acctid=acctids[0].text.strip()
	acctcur='any'
	m=re.search('^(.*)-([A-Z]{3})$',acctid)
	if m:
		acctid=m.group(1)
		acctcur=m.group(2)
	print "INFO: Account number:", acctid, "Currency:", acctcur

	missing_symbols=False
	secs=[]
	for sec in doc.findall('./SECLISTMSGSRSV1/SECLIST/*/SECINFO'):
		id=sec.findall('./SECID/UNIQUEID')[0].text.strip()
		type=sec.findall('./SECID/UNIQUEIDTYPE')[0].text.strip()
		name=sec.findall('./SECNAME')[0].text.strip()
		symbol=sec.findall('./TICKER')
		if len(symbol):
			symbol=symbol[0].text.strip()
		else:
			symbol=None
		if symbols_file:
			if id in symbols_file[type]:
				name=symbols_file[type][id]['name']
				symbol=symbols_file[type][id]['symbol']
			else:
				print "WARNING: Missing symbol for", type, id, name, symbol
				missing_symbols=True
		secs.append({'id': id, 'type': type, 'name': name, 'symbol': symbol})

	print "DEBUG: Found", len(secs), "commodities."

	sess=Session(gnucash_file)
	
	try:
		# Make sure all the commodities exist
		ct=sess.book.get_table()
		
		for ns in ['CUSIP','ISIN']:
			for c in ct.get_commodities(ns):
				matched_sec=None
				for sec in secs:
					if sec['type']==ns and sec['id']==c.get_cusip():
						sec['c']=c
						break

		missing_secs=False
		for i,sec in enumerate(secs):
			if not 'c' in sec:
				print 'WARNING: Missing commodity', sec['type'],sec['id'],sec['name'],sec['symbol']
				missing_secs=True
				
		if missing_secs or missing_symbols:
			print 'ERROR: Missing symbols or commodities, aborting.'
			return
		
		# Find GNC parent account
		root=sess.book.get_root_account()
		matched_accts=find_acct_by_number_and_currency(root,acctid,acctcur)
		if len(matched_accts)==0:
			from_iban=conv_iban(acctid)
			if from_iban:
				matched_accts=find_acct_by_number_and_currency(root,from_iban,acctcur)
			
		if len(matched_accts)!=1:
			print 'ERROR: No unique account this number/currency; found', len(matched_accts)
			return
		
		acct=matched_accts[0]
		print 'DEBUG: Found parent account:',acct.GetName()
		
		# Make sure the account has the appropriate stock accounts
		created_accounts=0
		for sec in secs:
			matched_acct=None
			for secacct in acct.get_children():
				if secacct.GetCommodity().get_instance()==sec['c'].get_instance():
					matched_acct=secacct
					break
			if not matched_acct:
				secacct=Account(sess.book)
				if secacct:
					secacct.SetName(sec['name'])
					secacct.SetType(gnucash.ACCT_TYPE_STOCK)
					secacct.SetCommodity(sec['c'])
					secacct.SetCode(sec['id'])
					acct.append_child(secacct)
					created_accounts+=1
					print 'DEBUG: Created Account',sec['name']
				else:
					print 'ERROR: Error creating Account',sec['name']
					
		print 'INFO:',len(secs),'accounts total',created_accounts,'created.'
		
		wait_for_backup_file(gnucash_file)
		sess.save()
	except BaseException as e:
		print 'ERROR:',e
	finally:    
		sess.end()
		sess.destroy()