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()
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()
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()
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()
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
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()
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()
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
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
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, }, )
#!/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()
# 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()
from gnucash import Session, Account 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 returns 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 gnucash_session = Session("file:/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()
session.destroy() quit() # Or try next invoice else: # No currebt invoice found so create a new invoice TODO need to lookup next free Invoice ID perhaps? print "Creating a new invoice" invoice = Invoice(book, 'TEST', gbp, customer ) # I know, need to check of this exists too!! But this is a test script so... # NB Gnucash will happily make another invoice/bill with the same ID! I think this is a bad thing. invoice.SetDateOpened(datetime.date.today()) if invoice: # Test code invoice.GetID() # Create a new entry and populate it. Normally there would be a loop for each entry required, reading from a csv or similar entry = gnucash.gnucash_business.Entry(book, invoice) entry.SetDate(datetime.date.today()) entry.SetDateEntered(datetime.date.today()) entry.SetDescription ("Some stuff I sold") entry.SetAction("Material") entry.SetInvAccount(income) entry.SetQuantity( GncNumeric(1) ) gnc_price = GncNumeric(1040, 100) ## = price x 100 then set denom to 100 entry.SetInvPrice(gnc_price) #entry.SetInvTaxTable(tax_table) entry.SetInvTaxIncluded(False) session.save() # session.end() session.destroy()
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()
#!/usr/bin/env python3 ## @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()
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
split3.SetValue(num2) split3.SetAccount(savings_acct) split3.SetParent(trans2) trans1.SetCurrency(cad) trans1.SetDate(14, 3, 2006) trans1.SetDescription("Groceries") trans2.SetCurrency(cad) trans2.SetDate(7, 11, 1995) trans2.SetDescription("Opening Savings Balance") split2 = Split(book) split2.SetAccount(savings_acct) split2.SetParent(trans1) split2.SetValue(num1.neg()) split4 = Split(book) split4.SetAccount(opening_acct) split4.SetParent(trans2) split4.SetValue(num2.neg()) trans1.CommitEdit() trans2.CommitEdit() session.save() session.end() session.destroy()
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.items() : 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
session.end() session.destroy() quit() # Or try next invoice else: # No currebt invoice found so create a new invoice TODO need to lookup next free Invoice ID perhaps? print "Creating a new invoice" invoice = Invoice( book, 'TEST', gbp, customer ) # I know, need to check of this exists too!! But this is a test script so... # NB Gnucash will happily make another invoice/bill with the same ID! I think this is a bad thing. invoice.SetDateOpened(datetime.date.today()) if invoice: # Test code invoice.GetID() # Create a new entry and populate it. Normally there would be a loop for each entry required, reading from a csv or similar entry = gnucash.gnucash_business.Entry(book, invoice) entry.SetDate(datetime.date.today()) entry.SetDateEntered(datetime.date.today()) entry.SetDescription("Some stuff I sold") entry.SetAction("Material") entry.SetInvAccount(income) entry.SetQuantity(GncNumeric(1)) gnc_price = GncNumeric(1040, 100) ## = price x 100 then set denom to 100 entry.SetInvPrice(gnc_price) #entry.SetInvTaxTable(tax_table) entry.SetInvTaxIncluded(False) session.save() # session.end() session.destroy()
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()
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, }, )
# command-line options gnucash_filename = argv[1] first_invoice_number = argv[2] description = argv[3] invoice_value = gnc_numeric_from_decimal(Decimal(argv[4])) customer_ids = argv[5:] #Check that the date is set correctly for a batch of invoices. #Generally, the date should be set to the beginning of each month #for the month of the billing period. invoice_date = datetime.date(2012, 8, 1) session = Session(gnucash_filename, is_new=False) session.save() # this seems to make a difference in more complex cases book = session.book root_account = book.get_root_account() commodity_table = book.get_table() USD = commodity_table.lookup('CURRENCY', 'USD') assets = root_account.lookup_by_name("Assets") receivables = assets.lookup_by_name("Accounts Receivable") # This assumes you have an income subaccount of the form "Income:Member Dues" # The lookup fails if the parent account is specified. # IE root_account.lookup_by_name("Income:Member Dues") does not work. income = root_account.lookup_by_name("Member Dues")
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, args.xml) 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()
#!/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()
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()
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()
from os.path import abspath from sys import argv import datetime from datetime import timedelta from gnucash import Session, Account, GncNumeric from gnucash.gnucash_business import Customer, Employee, Vendor, Job, \ Address, Invoice, Entry, TaxTable, TaxTableEntry, GNC_AMT_TYPE_PERCENT, \ GNC_DISC_PRETAX from gnucash.gnucash_core_c import \ ACCT_TYPE_ASSET, ACCT_TYPE_RECEIVABLE, ACCT_TYPE_INCOME, \ GNC_OWNER_CUSTOMER, ACCT_TYPE_LIABILITY s = Session(argv[1], is_new=True) # this seems to make a difference in more complex cases s.save() book = s.book root = book.get_root_account() commod_table = book.get_table() CAD = commod_table.lookup('CURRENCY', 'CAD') a = Account(book) root.append_child(a) a.SetName('Assets') a.SetType(ACCT_TYPE_ASSET) a.SetCommodity(CAD) a2 = Account(book) a.append_child(a2) a2.SetName('Recievables')