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 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 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()
class TestSession(TestCase): def test_create_empty_session(self): self.ses = Session() def test_session_deprecated_arguments(self): """use deprecated arguments ignore_lock, is_new, force_new""" self.ses = Session(ignore_lock=False, is_new=True, force_new=False) def test_session_mode(self): """use mode argument""" self.ses = Session(mode=SessionOpenMode.SESSION_NORMAL_OPEN) def test_session_with_new_file(self): """create Session with new xml file""" from tempfile import TemporaryDirectory from urllib.parse import urlunparse with TemporaryDirectory() as tempdir: uri = urlunparse(("xml", tempdir, "tempfile", "", "", "")) with Session(uri, SessionOpenMode.SESSION_NEW_STORE) as ses: pass # try to open nonexistent file without NEW mode - should raise Exception uri = urlunparse(("xml", tempdir, "tempfile2", "", "", "")) with Session() as ses: with self.assertRaises(GnuCashBackendException): ses.begin(uri, mode=SessionOpenMode.SESSION_NORMAL_OPEN) # try to open nonexistent file without NEW mode - should raise Exception # use deprecated arg is_new uri = urlunparse(("xml", tempdir, "tempfile2", "", "", "")) with Session() as ses: with self.assertRaises(GnuCashBackendException): ses.begin(uri, is_new=False) uri = urlunparse(("xml", tempdir, "tempfile3", "", "", "")) with Session() as ses: ses.begin(uri, mode=SessionOpenMode.SESSION_NEW_STORE) # test using deprecated args uri = urlunparse(("xml", tempdir, "tempfile4", "", "", "")) with Session() as ses: ses.begin(uri, is_new=True) def test_app_utils_get_current_session(self): from gnucash import _sw_app_utils self.ses_instance = _sw_app_utils.gnc_get_current_session() self.ses = Session(instance=self.ses_instance) self.assertIsInstance(obj=self.ses, cls=Session) def test_get_book_from_current_session(self): from gnucash import _sw_app_utils from gnucash import Book self.ses_instance = _sw_app_utils.gnc_get_current_session() self.ses = Session(instance=self.ses_instance) self.book = self.ses.get_book() self.assertIsInstance(obj=self.book, cls=Book)
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()
class TestSession(TestCase): def test_create_empty_session(self): self.ses = Session() def test_app_utils_get_current_session(self): from gnucash import _sw_app_utils self.ses_instance = _sw_app_utils.gnc_get_current_session() self.ses = Session(instance=self.ses_instance) self.assertIsInstance(obj=self.ses, cls=Session) def test_get_book_from_current_session(self): from gnucash import _sw_app_utils from gnucash import Book self.ses_instance = _sw_app_utils.gnc_get_current_session() self.ses = Session(instance=self.ses_instance) self.book = self.ses.get_book() self.assertIsInstance(obj=self.book, cls=Book)
#!/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()
class BookSession( TestCase ): def setUp(self): self.ses = Session() self.book = self.ses.get_book()
class GnuCash(): """ In "/home/user/.odontuxrc" lies the odontux configuration, that tells about gnucash. In order that several dentists may use odontux together, the "profissionnalaccounting_url", which specify the gnucash account place is store in the user (ROLE_DENTIST) session : "models.users.OdontuxUser.profissionnalaccounting_url". The inside of gnucash needs at least : * assets ( all of the dentist assets ) * receivables ( what the patient owes to the dentist ) * dentalfund ( what the patient paid to the dentist, but ain't usable right away ; needs to pass by the bank ) * incomes ( all of the dentist incomes ) * dentalincomes ( incomes coming from dental practice activity ) While coding the assistant/secretary salary, the buying of dental supplies and all, we'll add "outcomes", liabilities... See commands/compta.py for : * assets * dentalfund * check * cash * card * transfer * paypal """ def __init__(self, patient_id, dentist_id): self.parser = ConfigParser.ConfigParser() home = os.path.expanduser("~") self.parser.read(os.path.join(home, ".odontuxrc")) self.dentist = ( meta.session.query(users.OdontuxUser) .filter(users.OdontuxUser.id == dentist_id) .one() ) profissionnalaccounting_url = self.dentist.gnucash_url.encode("utf_8") if profissionnalaccounting_url[-3:] == "xml": self.gnucashtype = "xml" elif "postgresql" in profissionnalaccounting_url.split("://"): self.gnucashtype = "postgresql" else: self.gnucashtype = "xml" assets = constants.ASSETS receivables = constants.RECEIVABLES dentalfund = constants.DENTAL_FUND incomes = constants.INCOMES dentalincomes = constants.DENTAL_INCOMES # Precise on which patient we'll work on self.patient_id = patient_id self.patient = meta.session.query(administration.Patient).filter( administration.Patient.id == patient_id).one() # Set the gnucash patient_id self.gcpatient_id = "pat_" + str(self.patient_id) # Set up the Book for accounting self.gcsession = GCSession(profissionnalaccounting_url, True) self.book = self.gcsession.get_book() # set the currency we'll use currency = constants.GNUCASH_CURRENCY commod_tab = self.book.get_table() self.currency = commod_tab.lookup("CURRENCY", currency) # Set up the root on accounting book self.root = self.book.get_root_account() # Assets self.assets = self.root.lookup_by_name(assets) # What the patient owes to dental office self.receivables = self.assets.lookup_by_name(receivables) if not self.receivables.lookup_by_name(self.gcpatient_id): self.patient_receivables = Account(self.book) self.receivables.append_child(self.patient_receivables) self.patient_receivables.SetName(self.gcpatient_id) self.patient_receivables.SetType(gnc_core_c.ACCT_TYPE_RECEIVABLE) self.patient_receivables.SetCommodity(self.currency) else: self.patient_receivables = self.receivables.lookup_by_name( self.gcpatient_id) # What the patient paid to dental office, but not usable # because it needs to pass by the bank. # the detail of dental fund is build in commands/compta.py # while getting the paymenttype. self.dentalfund = self.assets.lookup_by_name(dentalfund) # Incomes self.incomes = self.root.lookup_by_name(incomes) self.dentalincomes = self.incomes.lookup_by_name(dentalincomes) def gnc_numeric_from_decimal(self, decimal_value): sign, digits, exponent = decimal_value.as_tuple() # convert decimal digits to a fractional numerator # equivlent to # numerator = int(''.join(digits)) # but without the wated conversion to string and back, # this is probably the same algorithm int() uses numerator = 0 TEN = int(Decimal(0).radix()) # this is always 10 numerator_place_value = 1 # add each digit to the final value multiplied by the place value # from least significant to most sigificant for i in xrange(len(digits)-1,-1,-1): numerator += digits[i] * numerator_place_value numerator_place_value *= TEN if decimal_value.is_signed(): numerator = -numerator # if the exponent is negative, we use it to set the denominator if exponent < 0 : denominator = TEN ** (-exponent) # if the exponent isn't negative, we bump up the numerator # and set the denominator to 1 else: numerator *= TEN ** exponent denominator = 1 return GncNumeric(numerator, denominator)
#!/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()
class BookSession(TestCase): def setUp(self): self.ses = Session() self.book = self.ses.get_book()
class BookSession(TestCase): def setUp(self): self.ses = Session() self.book = self.ses.get_book() table = self.book.get_table() self.currency = table.lookup('CURRENCY', 'EUR')
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
class BookSession( TestCase ): def setUp(self): self.ses = Session() self.book = self.ses.get_book() self.table = self.book.get_table() self.currency = self.table.lookup('CURRENCY', 'EUR')
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()
class TransactionListing(object): """ Extracts all transactions from a specific account in a GnuCash file and returns them as a list""" def load(self, filename): """ Load a GnuCash file, e.g. xml:///tmp/test.gnucash """ self.session = Session(filename, is_new=False, ignore_lock=True) self.book = self.session.get_book() def format_guid(self, gnc_guid): as_string = gnc_guid.to_string() uuid = UUID(as_string) return str(uuid) def get_all_transactions(self, account, positive_only=False): """ Yields a list of all transactions as a tuple. @param str account @return tuple(unix time stamp, memo, value as string) """ account = self.get_account(account) transactions = [] split_list = account.GetSplitList() def get_transaction_str_amount(transaction): return numeric_to_doublestr( transaction.GetAccountAmount(account)) # this temporary list is used so that duplicate transactions # can be detected (these appear when multiple splits of the # same transaction belong to the current account) gnc_transactions = [] known_transactions = set() for split in split_list: transaction = split.GetParent() amount = get_transaction_str_amount(transaction) if positive_only and amount[0] == '-': continue guid = self.format_guid(transaction.GetGUID()) if guid in known_transactions: continue known_transactions.add(guid) gnc_transactions.append(transaction) for transaction in gnc_transactions: guid = self.format_guid(transaction.GetGUID()) date = transaction.GetDate() desc = transaction.GetDescription().decode(CHARSET) amount = get_transaction_str_amount(transaction) if not desc.strip() and not float(amount): continue yield ( guid, date, desc, amount) def get_account(self, account): """ Looks up an account object by a given navigation string (e.g. Aktiva:Foo:Bar) @param str account @param GnuCash.Account account """ account_path = account.split(':') search = self.book.get_root_account() found = True while found and account_path: account_name = account_path.pop(0).lower() found = False for account in search.get_children(): if account.name.lower() == account_name: search = account found = True break if not found: raise RuntimeError("Account not found") return account