#!/usr/bin/env python3 # -*- coding: utf-8 -*- ##@file # @brief Exports an invoice to lco-file for use with LaTeX, see \ref py_invoice_export # @ingroup python_bindings_examples # @author Christoph Holtermann (c.holtermann (at) gmx.de) # @date May 2011 # # @details # The output file can be imported into KOMA-Script-letters. # This works primarily for germany. Internationalization welcome! # # Additional files: # # - Invoice.tex\n # Example template file. Should be modified according to personal needs. # - rechnung.sty\n # style file for invoices.\n # This file is not part of the python-bindings!\n # For an example where to get it see section credits below. # # Usage : # \code latex_invoice -l -f -n INVOICE_NUMBER file://testfile \endcode # will create file data.lco. # \code latex --output-format=pdf Invoice.tex \endcode # should run latex on file Invoice.tex and result in Invoice.pdf. Invoice.tex includes data.lco. # # Additional information : # # - Doxygen docs: see page \ref py_invoice_export at https://code.gnucash.org/docs/MAINT or MASTER # - https://www.uweziegenhagen.de/latex/documents/rechnung/rechnungen.pdf (german) # # Credits to and ideas from # # - Main function as proposed by Guido van Rossum # at https://www.artima.com/weblogs/viewpost.jsp?thread=4829 # - Invoice.tex is derived from\n # scrlttr2.tex v0.3. (c) by Juergen Fenn \n # https://www.komascript.de/node/355\n # english translation: ftp://ftp.dante.de/tex-archive/info/templates/fenn/scrlttr2en.tex # - rechnung.sty\n # from M G Berberich (berberic@fmi.uni-passau.de) and Ulrich Sibiller (uli42@web.de) # Ver3.10 from https://www.forwiss.uni-passau.de/~berberic/TeX/Rechnung/index.html # # To Do: # # - get own contact data from gnucash # - have own bank information in footline # - nicer formatting of invoice date and date due # - is there anything else missing in this invoice ? try: import sys import getopt import gnucash import str_methods from gncinvoicefkt import * from IPython import version_info as IPython_version_info if IPython_version_info[0] >= 1: from IPython.terminal.ipapp import TerminalIPythonApp else: from IPython.frontend.terminal.ipapp import TerminalIPythonApp from gnucash.gnucash_business import ( Customer, Employee, Vendor, Job, Address, Invoice, Entry, TaxTable, TaxTableEntry, GNC_AMT_TYPE_PERCENT, GNC_DISC_PRETAX, ) from gnucash import SessionOpenMode import locale except ImportError as import_error: print("Problem importing modules.") print(import_error) sys.exit(2) class Usage(Exception): def __init__(self, msg): self.msg = msg def invoice_to_lco(invoice): """returns a string which forms a lco-file for use with LaTeX""" lco_out = u"\ProvidesFile{data.lco}[]\n" def write_variable(ukey, uvalue, replace_linebreak=True): outstr = u"" if uvalue.endswith("\n"): uvalue = uvalue[0 : len(uvalue) - 1] if not ukey in [u"fromaddress", u"toaddress", u"date"]: outstr += u"\\newkomavar{" outstr += ukey outstr += u"}\n" outstr += u"\\setkomavar{" outstr += ukey outstr += u"}{" if replace_linebreak: outstr += uvalue.replace(u"\n", u"\\\\") + "}" return outstr # Write owners address add_str = u"" owner = invoice.GetOwner() if owner.GetName() != "": add_str += owner.GetName() + "\n" addr = owner.GetAddr() if addr.GetName() != "": add_str += addr.GetName() + "\n" if addr.GetAddr1() != "": add_str += addr.GetAddr1() + "\n" if addr.GetAddr2() != "": add_str += addr.GetAddr2() + "\n" if addr.GetAddr3() != "": add_str += addr.GetAddr3() + "\n" if addr.GetAddr4() != "": add_str += addr.GetAddr4() + "\n" lco_out += write_variable("toaddress2", add_str) # Invoice number inr_str = invoice.GetID() lco_out += write_variable("rechnungsnummer", inr_str) # date date = invoice.GetDatePosted() udate = date.strftime("%d.%m.%Y") lco_out += write_variable("date", udate) + "\n" # date due date_due = invoice.GetDateDue() udate_due = date_due.strftime("%d.%m.%Y") lco_out += write_variable("date_due", udate_due) + "\n" # Write the entries ent_str = u"" locale.setlocale(locale.LC_ALL, "") for n, ent in enumerate(invoice.GetEntries()): line_str = u"" if type(ent) != Entry: ent = Entry(instance=ent) # Add to method_returns_list descr = ent.GetDescription() price = ent.GetInvPrice().to_double() n = ent.GetQuantity() uprice = locale.currency(price).rstrip(" EUR") un = str( int(float(n.num()) / n.denom()) ) # choose best way to format numbers according to locale line_str = u"\Artikel{" line_str += un line_str += u"}{" line_str += descr line_str += u"}{" line_str += uprice line_str += u"}" # print(line_str) ent_str += line_str lco_out += write_variable("entries", ent_str) return lco_out def main(argv=None): if argv is None: argv = sys.argv try: prog_name = argv[0] with_ipshell = False ignore_lock = False no_latex_output = True list_invoices = False output_file_name = "data.lco" invoice_number = None try: opts, args = getopt.getopt(argv[1:], "fhiln:po:", ["help"]) except getopt.error as msg: raise Usage(msg) for opt in opts: if opt[0] in ["-f"]: print("ignoring lock") ignore_lock = True if opt[0] in ["-h", "--help"]: raise Usage("Help:") if opt[0] in ["-i"]: print("Using ipshell") with_ipshell = True if opt[0] in ["-l"]: print("listing all invoices") list_invoices = True if opt[0] in ["-n"]: invoice_number = int(opt[1]) print("using invoice number", invoice_number) no_latex_output = False if opt[0] in ["-o"]: output_file_name = opt[1] print("using output file", output_file_name) if len(args) > 1: print("opts:", opts, "args:", args) raise Usage("Only one input can be accepted !") if len(args) == 0: raise Usage("No input given !") input_url = args[0] except Usage as err: if err.msg == "Help:": retcode = 0 else: print("Error:", err.msg, file=sys.stderr) print("for help use --help", file=sys.stderr) retcode = 2 print("Generate a LaTeX invoice or print out all invoices.") print() print("Usage:") print() print("Invoke with", prog_name, "input.") print("where input is") print(" filename") print("or file://filename") print("or mysql://user:password@host/databasename") print() print("-f force open = ignore lock") print("-h or --help for this help") print("-i for ipython shell") print("-l list all invoices") print("-n number use invoice number (no. from previous run with -l)") print("-o name use name as outputfile. default: data.lco") return retcode # Try to open the given input try: session = gnucash.Session( input_url, SessionOpenMode.SESSION_READ_ONLY if ignore_lock else SessionOpenMode.SESSION_NORMAL_OPEN, ) except Exception as exception: print("Problem opening input.") print(exception) return 2 book = session.book root_account = book.get_root_account() comm_table = book.get_table() EUR = comm_table.lookup("CURRENCY", "EUR") invoice_list = get_all_invoices(book) if list_invoices: for number, invoice in enumerate(invoice_list): print(str(number) + ")") print(invoice) if not (no_latex_output): if invoice_number == None: print("Using the first invoice:") invoice_number = 0 invoice = invoice_list[invoice_number] print("Using the following invoice:") print(invoice) lco_str = invoice_to_lco(invoice) # Opening output file f = open(output_file_name, "w") f.write(lco_str) f.close() if with_ipshell: app = TerminalIPythonApp.instance() app.initialize(argv=[]) # argv=[] instructs IPython to ignore sys.argv app.start() # session.save() session.end() if __name__ == "__main__": sys.exit(main())