From c33270221d5257d14fd0bfaa6e97eefc9a2fe0b1 Mon Sep 17 00:00:00 2001 From: Mike Evans Date: Fri, 7 Jan 2011 14:04:55 +0000 Subject: [PATCH] A plugin to import CSV data as a new invoice or bill. Based on code written by Sebastian Held. Also includes a python script to massage a downloaded order form into the correct format for import. See contrib/rapid2gnucash.py This works with Rapid Electronics (UK) and can be used as a basis for other vendors. Users need to add a line in their ~/.gnucash/config.user for the module to be loaded: (gnc:module-load "gnucash/plugins/bi_import" 0) See bug #624911 for more details. git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@20034 57a11ea4-9604-0410-9ed3-97b8803252fd --- configure.ac | 37 +- contrib/rapid2gnucash.py | 74 ++ src/Makefile.am | 3 +- src/plugins/Makefile.am | 1 + src/plugins/bi_import/Makefile.am | 43 ++ src/plugins/bi_import/README | 5 + src/plugins/bi_import/bi_import.c | 710 ++++++++++++++++++ src/plugins/bi_import/bi_import.h | 75 ++ src/plugins/bi_import/glade/Makefile.am | 4 + src/plugins/bi_import/glade/bi_import.glade | 370 +++++++++ src/plugins/bi_import/gnc-plugin-bi_import.c | 115 +++ src/plugins/bi_import/gnc-plugin-bi_import.h | 78 ++ src/plugins/bi_import/gncmod-bi_import.c | 100 +++ src/plugins/bi_import/gui.c | 471 ++++++++++++ src/plugins/bi_import/gui.h | 48 ++ src/plugins/bi_import/helpers.c | 80 ++ src/plugins/bi_import/helpers.h | 32 + src/plugins/bi_import/regex.txt | 1 + src/plugins/bi_import/ui/Makefile.am | 5 + .../bi_import/ui/gnc-plugin-bi_import-ui.xml | 13 + 20 files changed, 2248 insertions(+), 17 deletions(-) create mode 100755 contrib/rapid2gnucash.py create mode 100644 src/plugins/Makefile.am create mode 100644 src/plugins/bi_import/Makefile.am create mode 100644 src/plugins/bi_import/README create mode 100644 src/plugins/bi_import/bi_import.c create mode 100644 src/plugins/bi_import/bi_import.h create mode 100644 src/plugins/bi_import/glade/Makefile.am create mode 100644 src/plugins/bi_import/glade/bi_import.glade create mode 100644 src/plugins/bi_import/gnc-plugin-bi_import.c create mode 100644 src/plugins/bi_import/gnc-plugin-bi_import.h create mode 100644 src/plugins/bi_import/gncmod-bi_import.c create mode 100644 src/plugins/bi_import/gui.c create mode 100644 src/plugins/bi_import/gui.h create mode 100644 src/plugins/bi_import/helpers.c create mode 100644 src/plugins/bi_import/helpers.h create mode 100644 src/plugins/bi_import/regex.txt create mode 100644 src/plugins/bi_import/ui/Makefile.am create mode 100644 src/plugins/bi_import/ui/gnc-plugin-bi_import-ui.xml diff --git a/configure.ac b/configure.ac index 61c1c84a16..d3a4bf70d5 100644 --- a/configure.ac +++ b/configure.ac @@ -240,7 +240,7 @@ update to latest darwin]) AC_MSG_CHECKING(For GDK-Quartz) platform=osx _gdk_tgt=`$PKG_CONFIG --variable=target gdk-2.0` - if test "x$_gdk_tgt" = xquartz; then + if test "x$_gdk_tgt" = xquartz; then platform=darwin/quartz AC_MSG_RESULT(yes) AC_DEFINE(GDK_QUARTZ,,[Using GDK Quartz (not X11)]) @@ -311,8 +311,8 @@ AM_CONDITIONAL(OS_WIN32, test "x$native_win32" = "xyes") # These are unavailable on windows/mingw32 and X11 isn't desired or # required for MacOSX Quartz -if test "x$_gdk_tgt" = xquartz; -then +if test "x$_gdk_tgt" = xquartz; +then AC_CHECK_HEADERS(glob.h) else AC_CHECK_HEADERS(X11/Xlib.h glob.h) @@ -321,7 +321,7 @@ AM_CONDITIONAL(HAVE_X11_XLIB_H, test "x$ac_cv_header_X11_Xlib_h" = "xyes") AC_CHECK_FUNCS(chown gethostname getppid getuid gettimeofday gmtime_r) AC_CHECK_FUNCS(gethostid link) ################################################## - + ### -------------------------------------------------------------------------- @@ -663,7 +663,7 @@ case "${want_ofx}" in esac if test x${want_ofx} != xno ; then - AC_ARG_WITH( ofx-prefix, + AC_ARG_WITH( ofx-prefix, [AS_HELP_STRING([--with-ofx-prefix=DIR],[specify where to look for libOFX])], OFXPREFIX="$with_ofx_prefix" ) @@ -698,7 +698,7 @@ then ] ) fi - if test "x${want_ofx}" != xno ; then + if test "x${want_ofx}" != xno ; then LIBOFX_LIBS="${LIBOFX_LIBS} -lofx" AC_MSG_CHECKING(for libofx) save_LIBS="${LIBS}" @@ -896,11 +896,11 @@ AC_DEFUN([BB_ENABLE_DOXYGEN], [ AC_ARG_ENABLE(doxygen, [AS_HELP_STRING([--enable-doxygen],[enable documentation generation with doxygen (auto)])]) AC_ARG_ENABLE(dot, [AS_HELP_STRING([--enable-dot],[use 'dot' to generate graphs in doxygen (auto)])]) - AC_ARG_ENABLE(html-docs, [AS_HELP_STRING([--enable-html-docs],[enable HTML generation with doxygen (yes)])], [], [ enable_html_docs=yes]) - AC_ARG_ENABLE(latex-docs, [AS_HELP_STRING([--enable-latex-docs],[enable LaTeX documentation generation with doxygen (no)])], [], [ enable_latex_docs=no]) + AC_ARG_ENABLE(html-docs, [AS_HELP_STRING([--enable-html-docs],[enable HTML generation with doxygen (yes)])], [], [ enable_html_docs=yes]) + AC_ARG_ENABLE(latex-docs, [AS_HELP_STRING([--enable-latex-docs],[enable LaTeX documentation generation with doxygen (no)])], [], [ enable_latex_docs=no]) if test "x$enable_doxygen" = xno; then enable_doc=no - else + else AC_PATH_PROG(DOXYGEN, doxygen, , $PATH) if test x$DOXYGEN = x; then if test "x$enable_doxygen" = xyes; then @@ -942,7 +942,7 @@ LIBS="$LIBS -lm" ### When disabled, we don't even need to check for them! ### -------------------------------------------------------------------------- -AC_ARG_ENABLE(gui, +AC_ARG_ENABLE(gui, [AS_HELP_STRING([--disable-gui],[build without the GNOME GUI components of Gnucash])], [case "${enableval}" in yes) gnc_build_gui=true ;; @@ -955,7 +955,7 @@ AM_CONDITIONAL(GNUCASH_ENABLE_GUI, test x${gnc_build_gui} = xtrue) ### -------------------------------------------------------------------------- ### GNOME gui components -- built by default, but not if --enable-gui=no -### or --disable-gui is passed to autogen +### or --disable-gui is passed to autogen ### -------------------------------------------------------------------------- if test x${gnc_build_gui} = xtrue ; @@ -1118,7 +1118,7 @@ then esac ### ---------------------------------------------------------------------- - + AC_ARG_ENABLE( efence, [AS_HELP_STRING([--enable-efence],[link using efence])], if test x$enableval = xyes; then @@ -1134,7 +1134,7 @@ then ## For now, we just presume you're using the GNOME version. The other ## UI's haven't been carried over during the automake transition. At ## some point, if it's deemed worthwhile, they can be resurrected... - + GNOME=1 AC_DEFINE(GNOME,,using GNOME) @@ -1293,7 +1293,7 @@ then *) AC_MSG_ERROR(bad value ${enableval} for --enable-error-on-warning) ;; esac], [ if test "${error_on_warning_as_default}" = "yes"; then - warnFLAGS="${warnFLAGS} -Werror"; + warnFLAGS="${warnFLAGS} -Werror"; gnc_error_on_warning=auto else gnc_error_on_warning=no @@ -1401,10 +1401,10 @@ AC_CONFIG_FILES( src/bin/test/Makefile src/core-utils/Makefile src/core-utils/test/Makefile - src/calculation/Makefile + src/calculation/Makefile src/calculation/test/Makefile src/debug/Makefile - src/debug/valgrind/Makefile + src/debug/valgrind/Makefile src/doc/Makefile src/doc/design/Makefile src/doc/xml/Makefile @@ -1495,6 +1495,11 @@ AC_CONFIG_FILES( src/business/business-gnome/ui/Makefile src/business/business-ledger/Makefile + dnl # Stuff for bill/invoice import plugin + src/plugins/Makefile + src/plugins/bi_import/Makefile + src/plugins/bi_import/glade/Makefile + src/plugins/bi_import/ui/Makefile dnl # non-makefiles dnl # Please read doc/build-system before adding *anything* here diff --git a/contrib/rapid2gnucash.py b/contrib/rapid2gnucash.py new file mode 100755 index 0000000000..59f9efd3fd --- /dev/null +++ b/contrib/rapid2gnucash.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python +''' +# rapid2gnucash.py +# +# Copyright 2010 Mike Evans +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. + +Convert a CSV file exported from Rapid Electronics (UK) to a form importable by GnuCash +Line format is: +line number,product code,quantity,availability,product description,unit price,discounts,line total,delivery,sub total,vat,grand total + +Useage: rapid2gnucash.py DOWNLOADED_BASKET.csv "ORDER_NUMBER" <"Expense account"> > output.csv + +We need to remove first line and totals + +Format needs to be: +#id,date_opened,vendor_id,billing_id,notes,date,desc,action,account,quantity,price,disc_type,disc_how,discount,taxable,taxincluded,tax_table,date_posted,due_date,account_posted,memo_posted,accu_splits, +Not all fields need to have values but the delimiters (,) do. +Some fields are compulsory: id, vendor_id, action, quantity, price, taxable +''' +import sys +import csv + +VENDOR_ID="000013" # Obviously this needs to match your vendor ID +try: + INFILE=sys.argv[1] +except: + print "No input files specified." + print "Useage: Useage: rapid2gnucash.py DOWNLOADED_BASKET.csv \"ORDER_NUMBER\"" + quit(1) +try: + INV_ID=sys.argv[2] +except: + print "No order number specified." + print "Useage: Useage: rapid2gnucash.py DOWNLOADED_BASKET.csv \"ORDER_NUMBER\"" + quit(1) +try: + ACCOUNT=sys.argv[3] +except: + ACCOUNT="Expenses:Materials General" # Default if absent on the command line. Edit to suit your account tree + +Reader = csv.reader(open(INFILE), delimiter=',') + +# Need to ignore 1st and last rows. +#Last row contains delivery, subtotal, VAT, total so these should be read too + +for row in Reader: + if row[0].isdigit(): # We only use numbered lines + outline=(INV_ID + ",," + VENDOR_ID + ",,,," + row[1] + " > " + row[4] + ",ea," + + ACCOUNT + "," + row[2] + "," + row[5].replace("GBP", "") + ",,,,no,,,,,,,,") + print outline + elif not row[0]: # Last row? Has empty first element + delivery = row[8].replace("GBP", "") + vat = row[10].replace("GBP", "") + outline=(INV_ID + ",," + VENDOR_ID + ",,,," + "DELIVERY" + ",ea," + + "Expenses:Postage and Delivery" + "," + "1" + "," + delivery + ",,,,no,,,,,,,,") + print outline # pipe to file for GnuCash import + outline=(INV_ID + ",," + VENDOR_ID + ",,,," + "VAT" + ",tax," + + "Expenses:VAT" + "," + "1" + "," + vat + ",,,,no,,,,,,,,") + print outline # pipe to file for GnuCash import diff --git a/src/Makefile.am b/src/Makefile.am index 1505a46bc6..cb216af79e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -25,13 +25,14 @@ GUI_SUBDIRS_2 = \ import-export \ business \ optional \ + plugins \ bin DIST_SUBDIRS = $(NONGUI_SUBDIRS) $(GUI_SUBDIRS_1) report $(GUI_SUBDIRS_2) if GNUCASH_ENABLE_GUI SUBDIRS = . $(DIST_SUBDIRS) -else +else SUBDIRS = . $(NONGUI_SUBDIRS) report endif diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am new file mode 100644 index 0000000000..b599da21f3 --- /dev/null +++ b/src/plugins/Makefile.am @@ -0,0 +1 @@ +SUBDIRS=bi_import diff --git a/src/plugins/bi_import/Makefile.am b/src/plugins/bi_import/Makefile.am new file mode 100644 index 0000000000..ecee766a0a --- /dev/null +++ b/src/plugins/bi_import/Makefile.am @@ -0,0 +1,43 @@ +SUBDIRS = ui glade . + +pkglib_LTLIBRARIES = libgncmod-bi_import.la + +libgncmod_bi_import_la_SOURCES = \ + gnc-plugin-bi_import.c \ + gncmod-bi_import.c \ + gui.c \ + helpers.c \ + bi_import.c + +noinst_HEADERS = \ + gnc-plugin-bi_import.h \ + gui.h \ + helpers.h \ + bi_import.h + +libgncmod_bi_import_la_LDFLAGS = -avoid-version + +libgncmod_bi_import_la_LIBADD = \ + ${top_builddir}/src/gnc-module/libgnc-module.la \ + ${GNOME_LIBS} \ + ${GLADE_LIBS} \ + ${QOF_LIBS} \ + ${GLIB_LIBS} + +AM_CFLAGS = \ + -I${top_srcdir}/src \ + -I${top_srcdir}/src/gnome \ + -I${top_srcdir}/src/register/ledger-core \ + -I${top_srcdir}/src/register/register-gnome \ + -I${top_srcdir}/src/register/register-core \ + -I${top_srcdir}/src/gnome-utils \ + -I${top_srcdir}/src/app-utils \ + -I${top_srcdir}/src/engine \ + -I${top_srcdir}/src/core-utils \ + -I${top_srcdir}/src/gnc-module \ + ${GNOME_CFLAGS} \ + ${GLADE_CFLAGS} \ + ${QOF_CFLAGS} \ + ${GLIB_CFLAGS} + +INCLUDES = -DG_LOG_DOMAIN=\"gnc.plugin.bi_import\" diff --git a/src/plugins/bi_import/README b/src/plugins/bi_import/README new file mode 100644 index 0000000000..1bc14ffbff --- /dev/null +++ b/src/plugins/bi_import/README @@ -0,0 +1,5 @@ +Things and thoughs AKA the TODO and README pages. + +BAD THINGS BAOUT THE IMPORTER +The importer should be limited to a singe invoice per file, perhaps creating a new invoice number each time. +Too many fields, and too inflexible field format. diff --git a/src/plugins/bi_import/bi_import.c b/src/plugins/bi_import/bi_import.c new file mode 100644 index 0000000000..febad70c07 --- /dev/null +++ b/src/plugins/bi_import/bi_import.c @@ -0,0 +1,710 @@ +/* + * bi_import.c -- + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, contact: + * + * Free Software Foundation Voice: +1-617-542-5942 + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 + * Boston, MA 02110-1301, USA gnu@gnu.org + */ + +/** + * @internal + * @file bi_import.c + * @brief core import functions for invoice import plugin + * @author Copyright (C) 2009 Sebastian Held + * @author Mike Evans + * @todo Create an option to import a pre-formed regex when it is present + * to enable the use of custom output csv formats. + * @todo Open the newly created invoice(es). + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +#include "gnc-ui.h" +#include "gnc-ui-util.h" +#include "gnc-gui-query.h" +#include "gncAddress.h" +#include "gncVendorP.h" +#include "gncVendor.h" +#include "gncEntry.h" + +#include "gnc-exp-parser.h" + +// query +#include "Query.h" +#include "qof.h" +#include "GNCId.h" +#include "gncIDSearch.h" +#include "bi_import.h" +#include "helpers.h" + + + + + +//#ifdef HAVE_GLIB_2_14 +// glib >= 2.14.0 +// perl regular expressions are available + +// this helper macro takes a regexp match and fills the model +#define FILL_IN_HELPER(match_name,column) \ + temp = g_match_info_fetch_named (match_info, match_name); \ + if (temp) \ + { \ + g_strstrip( temp ); \ + gtk_list_store_set (store, &iter, column, temp, -1); \ + g_free (temp); \ + } + + +bi_import_result +gnc_bi_import_read_file (const gchar * filename, const gchar * parser_regexp, + GtkListStore * store, guint max_rows, + bi_import_stats * stats) +{ + // some statistics + bi_import_stats stats_fallback; + FILE *f; + + // regexp + char *line; + gchar *line_utf8, *temp; + GMatchInfo *match_info; + GError *err; + GRegex *regexpat; + + // model + GtkTreeIter iter; + + f = g_fopen (filename, "rt"); + if (!f) + { + //gnc_error_dialog( 0, _("File %s cannot be opened."), filename ); + return RESULT_OPEN_FAILED; + } + + // set up statistics + if (!stats) + stats = &stats_fallback; + + // compile the regular expression and check for errors + err = NULL; + regexpat = + g_regex_new (parser_regexp, G_REGEX_EXTENDED | G_REGEX_OPTIMIZE, 0, &err); + if (err != NULL) + { + GtkWidget *dialog; + gchar *errmsg; + + errmsg = g_strdup_printf (_("Error in regular expression '%s':\n%s"), + parser_regexp, err->message); + g_error_free (err); + err = NULL; + + dialog = gtk_message_dialog_new (NULL, + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, "%s", errmsg); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + g_free (errmsg); + errmsg = 0; + + fclose (f); + return RESULT_ERROR_IN_REGEXP; + } + + // start the import + stats->n_imported = 0; + stats->n_ignored = 0; + stats->ignored_lines = g_string_new (NULL); +#define buffer_size 1000 + line = g_malloc0 (buffer_size); + while (!feof (f) + && ((max_rows == 0) + || (stats->n_imported + stats->n_ignored < max_rows))) + { + int l; + // read one line + if (!fgets (line, buffer_size, f)) + break; // eof + // now strip the '\n' from the end of the line + l = strlen (line); + if ((l > 0) && (line[l - 1] == '\n')) + line[l - 1] = 0; + + // convert line from locale into utf8 + line_utf8 = g_locale_to_utf8 (line, -1, NULL, NULL, NULL); + + // parse the line + match_info = NULL; // it seems, that in contrast to documentation, match_info is not alsways set -> g_match_info_free will segfault + if (g_regex_match (regexpat, line_utf8, 0, &match_info)) + { + // match found + stats->n_imported++; + + // fill in the values + gtk_list_store_append (store, &iter); + FILL_IN_HELPER (_("id"), ID); + FILL_IN_HELPER ("date_opened", DATE_OPENED); + FILL_IN_HELPER ("owner_id", OWNER_ID); + FILL_IN_HELPER ("biing_id", BILLING_ID); + FILL_IN_HELPER ("notes", NOTES); + + FILL_IN_HELPER ("date", DATE); + FILL_IN_HELPER ("desc", DESC); + FILL_IN_HELPER ("action", ACTION); + FILL_IN_HELPER ("account", ACCOUNT); + FILL_IN_HELPER ("quantity", QUANTITY); + FILL_IN_HELPER ("price", PRICE); + FILL_IN_HELPER ("disc_type", DISC_TYPE); + FILL_IN_HELPER ("disc_how", DISC_HOW); + FILL_IN_HELPER ("discount", DISCOUNT); + FILL_IN_HELPER ("taxable", TAXABLE); + FILL_IN_HELPER ("taxincluded", TAXINCLUDED); + FILL_IN_HELPER ("tax_table", TAX_TABLE); + + FILL_IN_HELPER ("date_posted", DATE_POSTED); + FILL_IN_HELPER ("due_date", DUE_DATE); + FILL_IN_HELPER ("account_posted", ACCOUNT_POSTED); + FILL_IN_HELPER ("memo_posted", MEMO_POSTED); + FILL_IN_HELPER ("accu_splits", ACCU_SPLITS); + } + else + { + // ignore line + stats->n_ignored++; + g_string_append (stats->ignored_lines, line_utf8); + g_string_append_c (stats->ignored_lines, '\n'); + } + + g_match_info_free (match_info); + match_info = 0; + g_free (line_utf8); + line_utf8 = 0; + } + g_free (line); + line = 0; + + g_regex_unref (regexpat); + regexpat = 0; + fclose (f); + + if (stats == &stats_fallback) + // stats are not requested -> free the string + g_string_free (stats->ignored_lines, TRUE); + + return RESULT_OK; +} + + +//! \brief try to fix some common errors in the csv representation of invoices +//! * corrects the date format +//! * corrects ambigous values in multi line invoices +//! * ensures customer exists +//! * if quantity is unset, set to 1 +//! * if price is unset, delete row +void +gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted, + GString * info) +{ + GtkTreeIter iter; + gboolean valid, row_deleted, row_fixed; + gchar *id, *date_opened, *date_posted, *owner_id, *date, *quantity, *price; + GString *prev_id, *prev_date_opened, *prev_date_posted, *prev_owner_id, *prev_date; // needed to fix multi line invoices + guint dummy; + + // allow the call to this function with only GtkListeStore* specified + if (!fixed) + fixed = &dummy; + if (!deleted) + deleted = &dummy; + + *fixed = 0; + *deleted = 0; + + // init strings + prev_id = g_string_new (""); + prev_date_opened = g_string_new (""); + prev_date_posted = g_string_new (""); + prev_owner_id = g_string_new (""); + prev_date = g_string_new (""); + + valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter); + while (valid) + { + row_deleted = FALSE; + row_fixed = FALSE; + + // Walk through the list, reading each row + gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, + ID, &id, + DATE_OPENED, &date_opened, + DATE_POSTED, &date_posted, + OWNER_ID, &owner_id, + DATE, &date, + QUANTITY, &quantity, PRICE, &price, -1); + + if (strlen (price) == 0) + { + // invalid row (no price given) + // no fix possible -> delete row + gtk_list_store_remove (store, &iter); + row_deleted = TRUE; + g_string_append_printf (info, + _("ROW DELETED, PRICE_NOT_SET: id=%s\n"), + id); + } + else if (strlen (quantity) == 0) + { + // invalid row (no quantity given) + // no fix possible -> delete row + gtk_list_store_remove (store, &iter); + row_deleted = TRUE; + g_string_append_printf (info, _("ROW DELETED, QTY_NOT_SET: id=%s\n"), + id); + } + else + { + if (strlen (id) == 0) + { + // no invoice id specified + if (prev_id->len == 0) + { + // cannot fix -> delete row + gtk_list_store_remove (store, &iter); + row_deleted = TRUE; + g_string_append_printf (info, + _("ROW DELETED, ID_NOT_SET\n")); + } + else + { + // this is a fixable multi line invoice + gtk_list_store_set (store, &iter, ID, prev_id->str, -1); + row_fixed = TRUE; + } + } + else + { + // remember invoice id (to be able to fix multi line invoices) + g_string_assign (prev_id, id); + // new invoice => reset all other fixable entries + g_string_assign (prev_date_opened, ""); + g_string_assign (prev_date_posted, ""); + g_string_assign (prev_owner_id, ""); + g_string_assign (prev_date, ""); + } + } + + if (!row_deleted) + { + // the row is valid (price and id are valid) + + if (strlen (date_opened) == 0) + { + if (prev_date_opened->len == 0) + { + // fix this by using the current date (why is this so complicated?) + gchar temp[20]; + GDate *date; + time_t secs; + struct tm now; + time (&secs); + localtime_r (&secs, &now); + date = + g_date_new_dmy (now.tm_mday, now.tm_mon + 1, + now.tm_year + 1900); + g_date_strftime (temp, 20, "%x", date); // create a locale specific date string + g_string_assign (prev_date_opened, temp); + g_date_free (date); + } + // fix this by using the previous date_opened value (multi line invoice) + gtk_list_store_set (store, &iter, DATE_OPENED, + prev_date_opened->str, -1); + row_fixed = TRUE; + } + else + { + // remember date_opened (to be able to fix multi line invoices) + g_string_assign (prev_date_opened, date_opened); + } + + // date_opened is valid + + if (strlen (date_posted) == 0) + { + if (prev_date_posted->len == 0) + { + // this invoice will have to get posted manually + } + else + { + // multi line invoice => fix it + gtk_list_store_set (store, &iter, DATE_POSTED, + prev_date_posted->str, -1); + row_fixed = TRUE; + } + } + else + { + // remember date_opened (to be able to fix multi line invoices) + g_string_assign (prev_date_posted, date_posted); + } + + // date_posted is valid + + if (strlen (quantity) == 0) + { + // quantity is unset => set to 1 + gtk_list_store_set (store, &iter, QUANTITY, "1", -1); + row_fixed = TRUE; + } + + // quantity is valid + + if (strlen (owner_id) == 0) + { + if (prev_owner_id->len == 0) + { + // no customer given and not fixable => delete row + gtk_list_store_remove (store, &iter); + row_deleted = TRUE; + g_string_append_printf (info, + _("ROW DELETED, VENDOR_NOT_SET: id=%s\n"), + id); + } + else + { + gtk_list_store_set (store, &iter, owner_id, + prev_owner_id->str, -1); + row_fixed = TRUE; + } + } + else + { + // remember owner_id + g_string_assign (prev_owner_id, owner_id); + } + // now check, if customer exists + if (!gnc_search_vendor_on_id + (gnc_get_current_book (), prev_owner_id->str)) + { + // customer not found => delete row + gtk_list_store_remove (store, &iter); + row_deleted = TRUE; + g_string_append_printf (info, + _("ROW DELETED, VENDOR_DOES_NOT_EXIST: id=%s\n"), + id); + } + + // owner_id is valid + } + + g_free (id); + g_free (date_opened); + g_free (date_posted); + g_free (owner_id); + g_free (date); + g_free (quantity); + g_free (price); + if (row_deleted) + { + (*deleted)++; + // reset all remembered values + g_string_assign (prev_id, ""); + g_string_assign (prev_date_opened, ""); + g_string_assign (prev_date_posted, ""); + g_string_assign (prev_owner_id, ""); + g_string_assign (prev_date, ""); + } + else if (row_fixed) + (*fixed)++; + valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter); + } + + // deallocate strings + g_string_free (prev_id, TRUE); + g_string_free (prev_date_opened, TRUE); + g_string_free (prev_date_posted, TRUE); + g_string_free (prev_owner_id, TRUE); + g_string_free (prev_date, TRUE); + + if (info && (info->len > 0)) + g_string_prepend (info, _("These rows were deleted:\n\n")); +} + + +/*********************************************************************** + * @todo Maybe invoice checking should be done in gnc_bi_import_fix_bis (...) + * rather than in here? But that is more concerned with ensuring the csv is consistent. + * @param GtkListStore *store + * @param guint *n_invoices_created + * @param guint *n_invoices_updated + * @return void + ***********************************************************************/ +void +gnc_bi_import_create_bis (GtkListStore * store, QofBook * book, + guint * n_invoices_created, + guint * n_invoices_updated, gchar * type) +{ + gboolean valid; + GtkTreeIter iter; + gchar *id, *date_opened, *owner_id, *biing_id, *notes; + gchar *date, *desc, *action, *account, *quantity, *price, *disc_type, + *disc_how, *discount, *taxable, *taxincluded, *tax_table; + gchar *date_posted, *due_date, *account_posted, *memo_posted, + *accumulatesplits; + guint dummy; + GncInvoice *invoice; + GncOrder *order; + GncEntry *entry; + gint day, month, year; + gnc_numeric n; + GncOwner *owner; + Account *acc; + enum update{YES = GTK_RESPONSE_YES, NO = GTK_RESPONSE_NO}update; + GtkWidget *dialog; + Timespec today; + + + // these arguments are needed + g_return_if_fail (store && book); + + // allow to call this function without statistics + if (!n_invoices_created) + n_invoices_created = &dummy; + if (!n_invoices_updated) + n_invoices_updated = &dummy; + *n_invoices_created = 0; + *n_invoices_updated = 0; + + invoice = NULL; + order = NULL; + update = NO; + + valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter); + while (valid) + { + // Walk through the list, reading each row + gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ID, &id, DATE_OPENED, &date_opened, DATE_POSTED, &date_posted, // if autoposting requested + DUE_DATE, &due_date, // if autoposting requested + ACCOUNT_POSTED, &account_posted, // if autoposting requested + MEMO_POSTED, &memo_posted, // if autoposting requested + ACCU_SPLITS, &accumulatesplits, // if autoposting requested + OWNER_ID, &owner_id, + BILLING_ID, &biing_id, + NOTES, ¬es, + DATE, &date, + DESC, &desc, + ACTION, &action, + ACCOUNT, &account, + QUANTITY, &quantity, + PRICE, &price, + DISC_TYPE, &disc_type, + DISC_HOW, &disc_how, + DISCOUNT, &discount, + TAXABLE, &taxable, + TAXINCLUDED, &taxincluded, + TAX_TABLE, &tax_table, -1); + + // TODO: Assign a new invoice number if one is absent. BUT we don't want to assign a new invoice for every line!! + // so we'd have to flag this up somehow or add an option in the import GUI. The former implies that we make + // an assumption about what the importer (person) wants to do. It seems resonable that a CSV file full of items with + // If an invoice exists then we add to it in this current schema. + // no predefined invoice number is a new invoice that's in need of a new number. + // This was not designed to satisfy the need for repeat invoices however, so maybe we need a another method for this, after all + // It should be easier to copy an invoice with a new ID than to go through all this malarky. + if (g_ascii_strcasecmp (type, "BILL")) + invoice = gnc_search_bill_on_id (book, id); + else if (g_ascii_strcasecmp (type, "INVOICE")) + invoice = gnc_search_invoice_on_id (book, id); + + if (!invoice) + { + // new invoice + invoice = gncInvoiceCreate (book); + gncInvoiceSetID (invoice, id); + owner = gncOwnerCreate (); + if (g_ascii_strcasecmp (type, "BILL") == 0) + gncOwnerInitVendor (owner, + gnc_search_vendor_on_id (book, owner_id)); + else if (g_ascii_strcasecmp (type, "INVOICE") == 0) + gncOwnerInitCustomer (owner, + gnc_search_customer_on_id (book, owner_id)); + gncInvoiceSetOwner (invoice, owner); + gncInvoiceSetCurrency (invoice, gncOwnerGetCurrency (owner)); // Set the invoice currency based on the owner + if (!(g_ascii_strcasecmp (type, ""))) // If a date is specified in CSV + { + qof_scan_date (date_opened, &day, &month, &year); + gncInvoiceSetDateOpened (invoice, + gnc_dmy2timespec (day, month, year)); + } + else // If no date in CSV + { + time_t now = time (NULL); + Timespec now_timespec; + timespecFromTime_t (&now_timespec, now); + gncInvoiceSetDateOpened (invoice, now_timespec); + } + gncInvoiceSetBillingID (invoice, biing_id); + gncInvoiceSetNotes (invoice, notes); + gncInvoiceSetActive (invoice, TRUE); + //if (g_ascii_strcasecmp(type,"INVOICE"))gncInvoiceSetBillTo( invoice, billto ); + (*n_invoices_created)++; + update = YES; + } +// I want to warn the user that an existing billvoice exists, but not every +// time. +// An import can contain many lines usually referring to the same invoice. +// NB: Posted invoices are NEVER updated. + else // if invoice exists + { + if (gncInvoiceIsPosted (invoice)) // Is it already posted? + { + valid = + gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter); + continue; // If already posted then never import + } + if (update != YES) // Pop up a dialog to ask if updates are the expected action + { + dialog = gtk_message_dialog_new (NULL, + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_YES_NO, + "%s", + _("Are you sure you have bills/invoices to update?")); + update = gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + if (update == NO) + { // Cleanup and leave + g_free (id); g_free (date_opened); g_free (owner_id); g_free (biing_id); + g_free (notes); g_free (date); g_free (desc); g_free (action); + g_free (account); g_free (quantity); g_free (price); g_free (disc_type); + g_free (disc_how); g_free (discount); g_free (taxable); g_free (taxincluded); + g_free (tax_table); g_free (date_posted); g_free (due_date); g_free (account_posted); + g_free (memo_posted); g_free (accumulatesplits); + return; + } + } + (*n_invoices_updated)++; + } + + + // add entry to invoice/bill + entry = gncEntryCreate (book); + qof_scan_date (date, &day, &month, &year); + gncEntrySetDate (entry, gnc_dmy2timespec (day, month, year)); + timespecFromTime_t (&today, time (NULL)); // set today to the current date + gncEntrySetDateEntered (entry, today); + gncEntrySetDescription (entry, desc); + gncEntrySetAction (entry, action); + + n = gnc_numeric_zero (); + gnc_exp_parser_parse (quantity, &n, NULL); + gncEntrySetQuantity (entry, n); + acc = gnc_account_lookup_for_register (gnc_get_current_root_account (), + account); + if (g_ascii_strcasecmp (type, "BILL") == 0) + { + gncEntrySetBillAccount (entry, acc); + n = gnc_numeric_zero (); + gnc_exp_parser_parse (price, &n, NULL); + gncEntrySetBillPrice (entry, n); + gncEntrySetBillTaxable (entry, text2bool (taxable)); + gncEntrySetBillTaxIncluded (entry, text2bool (taxincluded)); + gncEntrySetBillTaxTable (entry, + gncTaxTableLookupByName (book, tax_table)); + n = gnc_numeric_zero (); + gnc_exp_parser_parse (discount, &n, NULL); + gncBillAddEntry (invoice, entry); + } + else if (g_ascii_strcasecmp (type, "INVOICE") == 0) + { + gncEntrySetNotes (entry, notes); + gncEntrySetInvAccount (entry, acc); + n = gnc_numeric_zero (); + gnc_exp_parser_parse (price, &n, NULL); + gncEntrySetInvPrice (entry, n); + gncEntrySetInvTaxable (entry, text2bool (taxable)); + gncEntrySetInvTaxIncluded (entry, text2bool (taxincluded)); + gncEntrySetInvTaxTable (entry, + gncTaxTableLookupByName (book, tax_table)); + n = gnc_numeric_zero (); + gnc_exp_parser_parse (discount, &n, NULL); + gncEntrySetInvDiscount (entry, n); + gncEntrySetInvDiscountType (entry, text2disc_type (disc_type)); + gncEntrySetInvDiscountHow (entry, text2disc_how (disc_how)); + gncInvoiceAddEntry (invoice, entry); + } + valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter); + + // handle auto posting of invoices + { + gchar *new_id = NULL; + Transaction *tnx; + if (valid) + gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ID, &new_id, -1); + if (g_strcmp0 (id, new_id) != 0) + { + // the next invoice id is different => try to autopost this invoice + if (qof_scan_date (date_posted, &day, &month, &year)) + { + // autopost this invoice + Timespec d1, d2; + d1 = gnc_dmy2timespec (day, month, year); + qof_scan_date (due_date, &day, &month, &year); // obtains the due date, or leaves it at date_posted + d2 = gnc_dmy2timespec (day, month, year); + acc = gnc_account_lookup_for_register + (gnc_get_current_root_account (), account_posted); + tnx = gncInvoicePostToAccount (invoice, acc, &d1, &d2, + memo_posted, + text2bool (accumulatesplits)); + } + } + g_free (new_id); + } + + // cleanup + g_free (id); + g_free (date_opened); + g_free (owner_id); + g_free (biing_id); + g_free (notes); + g_free (date); + g_free (desc); + g_free (action); + g_free (account); + g_free (quantity); + g_free (price); + g_free (disc_type); + g_free (disc_how); + g_free (discount); + g_free (taxable); + g_free (taxincluded); + g_free (tax_table); + g_free (date_posted); + g_free (due_date); + g_free (account_posted); + g_free (memo_posted); + g_free (accumulatesplits); + } +} diff --git a/src/plugins/bi_import/bi_import.h b/src/plugins/bi_import/bi_import.h new file mode 100644 index 0000000000..93a241cb26 --- /dev/null +++ b/src/plugins/bi_import/bi_import.h @@ -0,0 +1,75 @@ +/* + * bi_import.h -- + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, contact: + * + * Free Software Foundation Voice: +1-617-542-5942 + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 + * Boston, MA 02110-1301, USA gnu@gnu.org + */ + +/** + * @addtogroup Tools + * @{ + * @file bi_import.h + * @brief core import functions for invoice import plugin + * @author Copyright (C) 2009 Sebastian Held + * @author Mike Evans + */ + +#ifndef GNC_PLUGIN_bi_import_bi_import_H +#define GNC_PLUGIN_bi_import_bi_import_H + +#include +#include + +G_BEGIN_DECLS + +// model +enum bi_import_model_columns { + ID, DATE_OPENED, OWNER_ID, BILLING_ID, NOTES, // invoice settings + DATE, DESC, ACTION, ACCOUNT, QUANTITY, PRICE, DISC_TYPE, DISC_HOW, DISCOUNT, TAXABLE, TAXINCLUDED, TAX_TABLE, // entry settings + DATE_POSTED, DUE_DATE, ACCOUNT_POSTED, MEMO_POSTED, ACCU_SPLITS, // autopost settings + N_COLUMNS +}; + +enum _bi_import_result { + RESULT_OK, + RESULT_OPEN_FAILED, + RESULT_ERROR_IN_REGEXP, +}; +typedef enum _bi_import_result bi_import_result; + +struct _bi_import_stats { + int n_imported, n_ignored; + GString *ignored_lines; +}; +typedef struct _bi_import_stats bi_import_stats; + + +bi_import_result +gnc_bi_import_read_file (const gchar *filename, const gchar *parser_regexp, GtkListStore *store, guint max_rows, bi_import_stats *stats); + +void +gnc_bi_import_fix_bis (GtkListStore *store, guint *fixed, guint *deleted, GString *info); + +void +gnc_bi_import_create_bis (GtkListStore *store, QofBook *book, guint *n_invoices_created, guint *n_invoices_updated, gchar *type); + + +G_END_DECLS + +#endif /* GNC_PLUGIN_bi_import_bi_import_H */ + +/** @} */ diff --git a/src/plugins/bi_import/glade/Makefile.am b/src/plugins/bi_import/glade/Makefile.am new file mode 100644 index 0000000000..242e770b73 --- /dev/null +++ b/src/plugins/bi_import/glade/Makefile.am @@ -0,0 +1,4 @@ +gladedir = $(GNC_GLADE_DIR) +glade_DATA = bi_import.glade + +EXTRA_DIST = ${glade_DATA} diff --git a/src/plugins/bi_import/glade/bi_import.glade b/src/plugins/bi_import/glade/bi_import.glade new file mode 100644 index 0000000000..85c34cd3fc --- /dev/null +++ b/src/plugins/bi_import/glade/bi_import.glade @@ -0,0 +1,370 @@ + + + + + + True + Import transactions from text file + dialog + + + + True + vertical + 8 + + + True + 3 + 0 + + + True + 12 + + + True + + + True + True + + + + 0 + + + + + gtk-open + True + True + False + True + + + + False + False + end + 1 + + + + + + + + + True + 1. Choose the file to import + True + + + label_item + + + + + False + 0 + + + + + True + 0 + + + True + 12 + + + True + + + True + + + Bill + True + False + True + False + True + Import bill CSV data + 0.4699999988079071 + True + True + + + + 0 + + + + + Invoice + True + False + False + True + Import invoice CSV data + True + radiobuttonBill + + + + 1 + + + + + + + + + + + + + + + + + True + 2, Select import type + True + + + label_item + + + + + 1 + + + + + True + 3 + 0 + + + True + 12 + + + True + vertical + + + Semicolon separated + True + True + False + True + True + + + + False + False + 0 + + + + + Comma separated + True + True + False + True + True + radiobutton1 + + + + False + False + 1 + + + + + Semicolon separated with quotes + True + True + False + True + True + radiobutton1 + + + + False + False + 2 + + + + + Comma separated with quotes + True + True + False + True + True + radiobutton1 + + + + False + False + 3 + + + + + Custom regular expression + True + True + False + True + True + radiobutton1 + + + + False + False + 4 + + + + + + + + + True + 3. Select import options + True + + + label_item + + + + + False + 2 + + + + + True + 3 + 0 + + + True + 12 + + + True + True + in + + + True + True + + + + + + + + + True + 4. Preview + True + + + label_item + + + + + 3 + + + + + True + end + + + gtk-help + -11 + True + True + True + False + True + + + + False + False + 0 + + + + + gtk-cancel + -6 + True + True + True + False + True + + + + False + False + 1 + + + + + gtk-ok + -5 + True + True + True + True + False + True + + + + False + False + 2 + + + + + False + end + 5 + + + + + + diff --git a/src/plugins/bi_import/gnc-plugin-bi_import.c b/src/plugins/bi_import/gnc-plugin-bi_import.c new file mode 100644 index 0000000000..b54187e5c0 --- /dev/null +++ b/src/plugins/bi_import/gnc-plugin-bi_import.c @@ -0,0 +1,115 @@ +/* + * gnc-plugin-bi_import.c -- + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, contact: + * + * Free Software Foundation Voice: +1-617-542-5942 + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 + * Boston, MA 02110-1301, USA gnu@gnu.org + */ + +/** + * @internal + * @file gnc-plugin-bi_import.c + * @brief Plugin registration of the bi_import plugin + * @author Copyright (C) 2009 Sebastian Held + */ + + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include + +#include "dialog-utils.h" + +#include "gnc-plugin-bi_import.h" +#include "gui.h" + +/* This static indicates the debugging module that this .o belongs to. */ +static QofLogModule log_module = G_LOG_DOMAIN; + +static void gnc_plugin_bi_import_class_init (GncPluginbi_importClass *klass); +static void gnc_plugin_bi_import_init (GncPluginbi_import *plugin); +static void gnc_plugin_bi_import_finalize (GObject *object); + +/* Command callbacks */ +static void gnc_plugin_bi_import_cmd_test (GtkAction *action, GncMainWindowActionData *data); + +#define PLUGIN_ACTIONS_NAME "gnc-plugin-bi_import-actions" +#define PLUGIN_UI_FILENAME "gnc-plugin-bi_import-ui.xml" + +static GtkActionEntry gnc_plugin_actions [] = { + /* Menu Items */ + { "ImportMenuAction", NULL, N_("_Import"), NULL, NULL, NULL }, + { "bi_importAction", NULL, N_("Import Bills & Invoices..."), NULL, N_("bi_import tooltip"), G_CALLBACK(gnc_plugin_bi_import_cmd_test) }, +}; +static guint gnc_plugin_n_actions = G_N_ELEMENTS(gnc_plugin_actions); + + +/************************************************************ + * Object Implementation * + ************************************************************/ + +G_DEFINE_TYPE(GncPluginbi_import, gnc_plugin_bi_import, GNC_TYPE_PLUGIN); + +GncPlugin * +gnc_plugin_bi_import_new (void) +{ + return GNC_PLUGIN (g_object_new (GNC_TYPE_PLUGIN_bi_import, (gchar*) NULL)); +} + +static void +gnc_plugin_bi_import_class_init (GncPluginbi_importClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GncPluginClass *plugin_class = GNC_PLUGIN_CLASS(klass); + + object_class->finalize = gnc_plugin_bi_import_finalize; + + /* plugin info */ + plugin_class->plugin_name = GNC_PLUGIN_bi_import_NAME; + + /* widget addition/removal */ + plugin_class->actions_name = PLUGIN_ACTIONS_NAME; + plugin_class->actions = gnc_plugin_actions; + plugin_class->n_actions = gnc_plugin_n_actions; + plugin_class->ui_filename = PLUGIN_UI_FILENAME; +} + +static void +gnc_plugin_bi_import_init (GncPluginbi_import *plugin) +{ +} + +static void +gnc_plugin_bi_import_finalize (GObject *object) +{ +} + +/************************************************************ + * Command Callbacks * + ************************************************************/ + +static void +gnc_plugin_bi_import_cmd_test (GtkAction *action, GncMainWindowActionData *data) +{ + ENTER ("action %p, main window data %p", action, data); + g_message ("bi_import"); + + gnc_plugin_bi_import_showGUI(); + + LEAVE (" "); +} diff --git a/src/plugins/bi_import/gnc-plugin-bi_import.h b/src/plugins/bi_import/gnc-plugin-bi_import.h new file mode 100644 index 0000000000..7e244af36d --- /dev/null +++ b/src/plugins/bi_import/gnc-plugin-bi_import.h @@ -0,0 +1,78 @@ +/* + * gnc-plugin-bi_import.h -- + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, contact: + * + * Free Software Foundation Voice: +1-617-542-5942 + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 + * Boston, MA 02110-1301, USA gnu@gnu.org + */ + +/** + * @addtogroup Tools + * @{ + * @file gnc-plugin-bi_import.h + * @brief Plugin registration of the bi_import module + * @author Copyright (C) 2009 Sebastian Held + */ + +#ifndef GNC_PLUGIN_bi_import_H +#define GNC_PLUGIN_bi_import_H + +#include + +#include "gnc-plugin.h" + +G_BEGIN_DECLS + +/* type macros */ +#define GNC_TYPE_PLUGIN_bi_import (gnc_plugin_bi_import_get_type()) +#define GNC_PLUGIN_bi_import(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNC_TYPE_PLUGIN_bi_import, GncPluginbi_import)) +#define GNC_PLUGIN_bi_import_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNC_TYPE_PLUGIN_bi_import, GncPluginbi_importClass)) +#define GNC_IS_PLUGIN_bi_import(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNC_TYPE_PLUGIN_bi_import)) +#define GNC_IS_PLUGIN_bi_import_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GNC_TYPE_PLUGIN_bi_import)) +#define GNC_PLUGIN_bi_import_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GNC_TYPE_PLUGIN_bi_import, GncPluginbi_importClass)) + +#define GNC_PLUGIN_bi_import_NAME "gnc-plugin-bi_import" + +/* typedefs & structures */ +typedef struct { + GncPlugin gnc_plugin; +} GncPluginbi_import; + +typedef struct { + GncPluginClass gnc_plugin; +} GncPluginbi_importClass; + +/* function prototypes */ +/** + * @return The glib runtime type of an bi_import plugin page + **/ +GType gnc_plugin_bi_import_get_type (void); + +/** + * @return A new GncPluginbi_import object + */ +GncPlugin* gnc_plugin_bi_import_new (void); + +/** + * Create a new GncPluginbi_import object and register it. + */ +void gnc_plugin_bi_import_create_plugin (void); + +G_END_DECLS + +/** @} */ + +#endif /* GNC_PLUGIN_bi_import_H */ diff --git a/src/plugins/bi_import/gncmod-bi_import.c b/src/plugins/bi_import/gncmod-bi_import.c new file mode 100644 index 0000000000..dbbf6b645e --- /dev/null +++ b/src/plugins/bi_import/gncmod-bi_import.c @@ -0,0 +1,100 @@ +/********************************************************************* + * gncmod-bi_import.c + * module definition/initialization for the bi_import GNOME UI module + * + * Copyright (c) 2009 Sebastian Held + * Copyright (c) 2001 Derek Atkins + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, contact: + * + * Free Software Foundation Voice: +1-617-542-5942 + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 + * Boston, MA 02110-1301, USA gnu@gnu.org + * + *********************************************************************/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "gnc-hooks.h" +#include "gnc-module.h" +#include "gnc-module-api.h" + +#include "gnc-plugin-manager.h" +#include "gnc-plugin-bi_import.h" + +GNC_MODULE_API_DECL(libgncmod_bi_import); + +/* version of the gnc module system interface we require */ +int libgncmod_bi_import_gnc_module_system_interface = 0; + +/* module versioning uses libtool semantics. */ +int libgncmod_bi_import_gnc_module_current = 0; +int libgncmod_bi_import_gnc_module_revision = 0; +int libgncmod_bi_import_gnc_module_age = 0; + + +char * +libgncmod_bi_import_gnc_module_path (void) +{ + return g_strdup("gnucash/plugins/bi_import"); +} + +char * +libgncmod_bi_import_gnc_module_description (void) +{ + return g_strdup("The GnuCash bi_import plugin"); +} + +int +libgncmod_bi_import_gnc_module_init (int refcount) +{ + if (!gnc_module_load ("gnucash/app-utils", 0)) { + return FALSE; + } + if (!gnc_module_load ("gnucash/gnome-utils", 0)) { + return FALSE; + } + if (!gnc_module_load ("gnucash/business-core", 0)) { + return FALSE; + } + if (!gnc_module_load ("gnucash/engine", 0)) { + return FALSE; + } + + if (refcount == 0) { + /* this is the first time the module is loaded */ + + gnc_plugin_manager_add_plugin ( gnc_plugin_manager_get (), + gnc_plugin_bi_import_new ()); + } + + return TRUE; +} + +int +libgncmod_bi_import_gnc_module_end (int refcount) +{ + if (refcount == 0) { + /* this is the last time the module is unloaded */ + } + + return TRUE; +} diff --git a/src/plugins/bi_import/gui.c b/src/plugins/bi_import/gui.c new file mode 100644 index 0000000000..069a89f6e9 --- /dev/null +++ b/src/plugins/bi_import/gui.c @@ -0,0 +1,471 @@ +/* + * gui.c -- + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, contact: + * + * Free Software Foundation Voice: +1-617-542-5942 + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 + * Boston, MA 02110-1301, USA gnu@gnu.org + */ + +/** + * @internal + * @file gui.c + * @brief GUI handling for bi import plugin + * @author Copyright (C) 2009 Sebastian Held + * @author Mike Evans + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "gnc-ui.h" +#include "gnc-ui-util.h" +#include "gnc-component-manager.h" +#include "dialog-utils.h" +#include "gnc-gui-query.h" +#include "gnc-file.h" +#include "gnc-gnome-utils.h" +#include "bi_import.h" +#include "gui.h" + +struct _bi_import_gui { + GtkWidget *dialog; + GtkWidget *tree_view; + GtkWidget *entryFilename; + GtkListStore *store; + gint component_id; + GString *regexp; + QofBook *book; + gchar *type; +}; + + + + + +// callback routines +void gnc_bi_import_gui_ok_cb (GtkWidget *widget, gpointer data); +void gnc_bi_import_gui_cancel_cb (GtkWidget *widget, gpointer data); +void gnc_bi_import_gui_help_cb (GtkWidget *widget, gpointer data); +void gnc_bi_import_gui_destroy_cb (GtkWidget *widget, gpointer data); +static void gnc_bi_import_gui_close_handler (gpointer user_data); +void gnc_bi_import_gui_buttonOpen_cb (GtkWidget *widget, gpointer data); +void gnc_bi_import_gui_filenameChanged_cb (GtkWidget *widget, gpointer data); +void gnc_bi_import_gui_option1_cb (GtkWidget *widget, gpointer data); +void gnc_bi_import_gui_option2_cb (GtkWidget *widget, gpointer data); +void gnc_bi_import_gui_option3_cb (GtkWidget *widget, gpointer data); +void gnc_bi_import_gui_option4_cb (GtkWidget *widget, gpointer data); +void gnc_bi_import_gui_option5_cb (GtkWidget *widget, gpointer data); +void gnc_import_gui_type(GtkWidget *widget, gpointer data); + +// utils +static gchar *gnc_input_dialog (GtkWidget *parent, const gchar *title, const gchar *msg, const gchar *default_input); +static void gnc_info2_dialog (GtkWidget *parent, const gchar *title, const gchar *msg); + + +BillImportGui * +gnc_plugin_bi_import_showGUI(void) +{ + BillImportGui *gui; + GladeXML *xml; + GList *glist; + GtkTreeIter iter; + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + + + + // if window exists already, activate it + glist = gnc_find_gui_components ("dialog-bi_import_gui", NULL, NULL); + if (glist) { + // window found + gui = g_list_nth_data (glist,0); + g_list_free (glist); + gtk_window_present (GTK_WINDOW(gui->dialog)); + return gui; + } + + // create new window + gui = g_new0 (BillImportGui, 1); + gui->type = "BILL"; // Set default type to match gui. really shouldn't be here TODO change me + xml = gnc_glade_xml_new ("bi_import.glade", "bi_import Dialog"); + gui->dialog = glade_xml_get_widget (xml, "bi_import Dialog"); + gui->tree_view = glade_xml_get_widget (xml, "treeview1"); + gui->entryFilename = glade_xml_get_widget (xml, "entryFilename"); + + gui->book = gnc_get_current_book(); + + gui->regexp = g_string_new ( "^(?[^;]*);(?[^;]*);(?[^;]*);(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*)(;?(?[^;]*)(;?(?[^;]*)(;?(?[^;]*)(;?(?[^;]*)(;?(?[^;]*)(;?(?[^;]*)(;(?[^;]*)(;(?[^;]*)(;(?[^;]*)(;(?[^;]*)(;(?[^;]*))?)?)?)?)?)?)?)?)?)?)?"); + + // create model and bind to view + gui->store = gtk_list_store_new (N_COLUMNS, + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, // invoice settings + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, // entry settings + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); // autopost settings + gtk_tree_view_set_model( GTK_TREE_VIEW(gui->tree_view), GTK_TREE_MODEL(gui->store) ); +#define CREATE_COLUMN(description,column_id) \ + renderer = gtk_cell_renderer_text_new (); \ + column = gtk_tree_view_column_new_with_attributes (description, renderer, "text", column_id, NULL); \ + gtk_tree_view_column_set_resizable (column, TRUE); \ + gtk_tree_view_append_column (GTK_TREE_VIEW (gui->tree_view), column); + CREATE_COLUMN ("id", ID); + CREATE_COLUMN ("date__opened", DATE_OPENED); + CREATE_COLUMN ("owner__id", OWNER_ID); + CREATE_COLUMN ("billing_id", BILLING_ID); + CREATE_COLUMN ("notes", NOTES); + + CREATE_COLUMN ("date", DATE); + CREATE_COLUMN ("desc", DESC); + CREATE_COLUMN ("action", ACTION); + CREATE_COLUMN ("account", ACCOUNT); + CREATE_COLUMN ("quantity", QUANTITY); + CREATE_COLUMN ("price", PRICE); + CREATE_COLUMN ("disc__type", DISC_TYPE); + CREATE_COLUMN ("disc__how", DISC_HOW); + CREATE_COLUMN ("discount", DISCOUNT); + CREATE_COLUMN ("taxable", TAXABLE); + CREATE_COLUMN ("taxincluded", TAXINCLUDED); + CREATE_COLUMN ("tax__table", TAX_TABLE); + + CREATE_COLUMN ("date__posted", DATE_POSTED); + CREATE_COLUMN ("due__date", DUE_DATE); + CREATE_COLUMN ("account__posted", ACCOUNT_POSTED); + CREATE_COLUMN ("memo__posted", MEMO_POSTED); + CREATE_COLUMN ("accu__splits", ACCU_SPLITS); + + gui->component_id = gnc_register_gui_component ("dialog-bi_import_gui", + NULL, + gnc_bi_import_gui_close_handler, + gui); + + /* Setup signals */ + glade_xml_signal_autoconnect_full( xml, gnc_glade_autoconnect_full_func, gui ); + + gtk_widget_show_all ( gui->dialog ); + return gui; +} + +static gchar * +gnc_plugin_bi_import_getFilename(void) +{ + // prepare file import dialog + gchar *filename; + GList *filters; + GtkFileFilter *filter; + filters = NULL; + filter = gtk_file_filter_new (); + gtk_file_filter_set_name (filter, "comma separated values (*.csv)"); + gtk_file_filter_add_pattern (filter, "*.csv"); + filters = g_list_append( filters, filter ); + filter = gtk_file_filter_new (); + gtk_file_filter_set_name (filter, "text files (*.txt)"); + gtk_file_filter_add_pattern (filter, "*.txt"); + filters = g_list_append( filters, filter ); + filename = gnc_file_dialog(_("Import Bills or Invoices from csv"), filters, NULL, GNC_FILE_DIALOG_IMPORT); + + return filename; +} + +void +gnc_bi_import_gui_ok_cb (GtkWidget *widget, gpointer data) +{ + BillImportGui *gui = data; + gchar *filename = g_strdup( gtk_entry_get_text( GTK_ENTRY(gui->entryFilename) ) ); + bi_import_stats stats; + bi_import_result res; + guint n_fixed, n_deleted, n_invoices_created, n_invoices_updated; + GString *info; + + // import + info = g_string_new(""); + + gtk_list_store_clear (gui->store); + res = gnc_bi_import_read_file (filename, gui->regexp->str, gui->store, 0, &stats); + if (res == RESULT_OK) { + gnc_bi_import_fix_bis (gui->store, &n_fixed, &n_deleted, info); + if (info->len > 0) + gnc_info_dialog (gui->dialog, "%s", info->str); + g_string_free( info, TRUE ); + gnc_bi_import_create_bis (gui->store, gui->book, &n_invoices_created, &n_invoices_updated, gui->type); + gnc_info_dialog (gui->dialog, _("Import results:\n%i lines were ignored\n%i lines imported:\n %u fixes\n %u ignored (not fixable)\n\n %u created\n %u updated (based on id)"), stats.n_ignored, stats.n_imported, n_fixed, n_deleted, n_invoices_created, n_invoices_updated); + + if (stats.n_ignored > 0) + gnc_info2_dialog (gui->dialog, _("These lines were ignored during import"), stats.ignored_lines->str); + + g_string_free (stats.ignored_lines,TRUE); + gnc_close_gui_component (gui->component_id); + } else + if (res == RESULT_OPEN_FAILED) { + gnc_error_dialog (gui->dialog, _("The input file can not be opened.")); + } else + if (res == RESULT_ERROR_IN_REGEXP) { + //gnc_error_dialog (gui->dialog, "The regular expression is faulty:\n\n%s", stats.err->str); + } +} + +void +gnc_bi_import_gui_cancel_cb (GtkWidget *widget, gpointer data) +{ + BillImportGui *gui = data; + + gnc_close_gui_component (gui->component_id); +} + +void +gnc_bi_import_gui_help_cb (GtkWidget *widget, gpointer data) +{ + gnc_gnome_help(HF_HELP, HL_USAGE); +} + +static void +gnc_bi_import_gui_close_handler (gpointer user_data) +{ + BillImportGui *gui = user_data; + + gtk_widget_destroy (gui->dialog); + // gui has already been freed by this point. + // gui->dialog = NULL; +} + +void +gnc_bi_import_gui_destroy_cb (GtkWidget *widget, gpointer data) +{ + BillImportGui *gui = data; + + gnc_suspend_gui_refresh (); + gnc_unregister_gui_component (gui->component_id); + gnc_resume_gui_refresh (); + + g_object_unref (gui->store); + g_string_free (gui->regexp, TRUE); + g_free (gui); +} + +void gnc_bi_import_gui_buttonOpen_cb (GtkWidget *widget, gpointer data) +{ + gchar *filename; + BillImportGui *gui = data; + + filename = gnc_plugin_bi_import_getFilename(); + if (filename) { + //printf("Setting filename"); // debug + gtk_entry_set_text( GTK_ENTRY(gui->entryFilename), filename ); + //printf("Set filename"); // debug + g_free( filename ); + } +} + +void gnc_bi_import_gui_filenameChanged_cb (GtkWidget *widget, gpointer data) +{ + BillImportGui *gui = data; + gchar *filename = g_strdup( gtk_entry_get_text( GTK_ENTRY(gui->entryFilename) ) ); + + // generate preview + gtk_list_store_clear (gui->store); + gnc_bi_import_read_file (filename, gui->regexp->str, gui->store, 10, NULL); + + g_free( filename ); +} + +void gnc_bi_import_gui_option1_cb (GtkWidget *widget, gpointer data) +{ + BillImportGui *gui = data; + if (!gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(widget) )) + return; + g_string_assign (gui->regexp, "^(?[^!#+^;]*);(?[^;]*);(?[^;]*);(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);(?[^;]*);(?[^;]*);(?[^;]*);(?[^;]*);(?[^;]*)"); + gnc_bi_import_gui_filenameChanged_cb (gui->entryFilename, gui); +} +void gnc_bi_import_gui_option2_cb (GtkWidget *widget, gpointer data) +{ + BillImportGui *gui = data; + if (!gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(widget) )) + return; + g_string_assign (gui->regexp, "^(?[^!#+^,]*),(?[^,]*),(?[^,]*),(?[^,]*),?(?[^,]*),?(?[^,]*),?(?[^,]*),?(?[^,]*),?(?[^,]*),?(?[^,]*),?(?[^,]*),?(?[^,]*),?(?[^,]*),?(?[^,]*),?(?[^,]*),?(?[^,]*),?(?[^,]*),(?[^,]*),(?[^,]*),(?[^,]*),(?[^,]*),(?[^,]*)"); + gnc_bi_import_gui_filenameChanged_cb (gui->entryFilename, gui); +} +void gnc_bi_import_gui_option3_cb (GtkWidget *widget, gpointer data) +{ + BillImportGui *gui = data; + if (!gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(widget) )) + return; + g_string_assign (gui->regexp, "^(?[^!#+^;]*);(?[^;]*);(?[^;]*);(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);(?[^;]*);(?[^;]*);(?[^;]*);(?[^;]*);(?[^;]*)"); + gnc_bi_import_gui_filenameChanged_cb (gui->entryFilename, gui); +} +void gnc_bi_import_gui_option4_cb (GtkWidget *widget, gpointer data) +{ + BillImportGui *gui = data; + if (!gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(widget) )) + return; + g_string_assign (gui->regexp, "^(?[^!#+^;]*);(?[^;]*);(?[^;]*);(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);?(?[^;]*);(?[^;]*);(?[^;]*);(?[^;]*);(?[^;]*);(?[^;]*)"); + gnc_bi_import_gui_filenameChanged_cb (gui->entryFilename, gui); +} +void gnc_bi_import_gui_option5_cb (GtkWidget *widget, gpointer data) +{ + BillImportGui *gui = data; + gchar *temp; + if (!gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(widget) )) + return; + temp = gnc_input_dialog (0, _("Adjust regular expression used for import"), _("This regular expression is used to parse the import file. Modify according to your needs.\n"), gui->regexp->str); + if (temp) { + g_string_assign (gui->regexp,temp); + g_free (temp); + gnc_bi_import_gui_filenameChanged_cb (gui->entryFilename, gui); + } +} +/***************************************************************** + * Set whether we are importing a bi, invoice, Customer or Vendor + * ****************************************************************/ + +void gnc_import_gui_type(GtkWidget *widget, gpointer data) +{ + BillImportGui *gui = data; + if (!gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(widget) )) + return; + if (g_ascii_strcasecmp(widget->name,"radiobuttonInvoice") == 0)gui->type="INVOICE"; + else if (g_ascii_strcasecmp(widget->name,"radiobuttonBill") == 0)gui->type="BILL"; + //printf ("TYPE set to, %s\n",gui->type); + +} + + + + + + + + + +/********************************************************************\ + * gnc_input_dialog * + * simple convenience dialog to get a single value from the user * + * user may choose between "Ok" and "Cancel" * + * * + * NOTE: This function does not return until the dialog is closed * + * * + * Args: parent - the parent window or NULL * + * title - the title of the dialog * + * msg - the message to display * + * default_input - will be displayed as default input * + * Return: the input (text) the user entered, if pressed "Ok" * + * NULL, if pressed "Cancel" * +\********************************************************************/ +static gchar * +gnc_input_dialog (GtkWidget *parent, const gchar *title, const gchar *msg, const gchar *default_input) +{ + GtkWidget *dialog, *label, *content_area; + gint result; + GtkWidget *view; + GtkTextBuffer *buffer; + gchar *user_input; + GtkTextIter start, end; + + /* Create the widgets */ + dialog = gtk_dialog_new_with_buttons (title, GTK_WINDOW (parent), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, + GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, + NULL); +#ifdef HAVE_GTK_2_14 + content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); +#else + content_area = GTK_DIALOG (dialog)->vbox; +#endif + + // add a label + label = gtk_label_new (msg); + gtk_container_add (GTK_CONTAINER (content_area), label); + + // add a textview + view = gtk_text_view_new (); + gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view), GTK_WRAP_WORD_CHAR); + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); + gtk_text_buffer_set_text (buffer, default_input, -1); + gtk_container_add (GTK_CONTAINER (content_area), view); + + // run the dialog + gtk_widget_show_all (dialog); + result = gtk_dialog_run (GTK_DIALOG (dialog)); + + if (result == GTK_RESPONSE_REJECT) + user_input = 0; + else { + gtk_text_buffer_get_start_iter (buffer, &start); + gtk_text_buffer_get_end_iter (buffer, &end); + user_input = gtk_text_buffer_get_text (buffer, + &start, &end, FALSE); + } + + gtk_widget_destroy (dialog); + + return user_input; +} + +/********************************************************************\ + * gnc_info2_dialog * + * displays an information dialog box (with scrollable text area) * + * * + * NOTE: This function does not return until the dialog is closed * + * * + * Args: parent - the parent window or NULL * + * title - the title of the dialog * + * msg - the message to display * + * Return: none * +\********************************************************************/ +static void +gnc_info2_dialog (GtkWidget *parent, const gchar *title, const gchar *msg) +{ + GtkWidget *dialog, *scrolledwindow, *content_area; + gint result; + GtkWidget *view; + GtkTextBuffer *buffer; + gchar *user_input; + GtkTextIter start, end; + gint width,height; + + /* Create the widgets */ + dialog = gtk_dialog_new_with_buttons (title, GTK_WINDOW (parent), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, + NULL); +#ifdef HAVE_GTK_2_14 + content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); +#else + content_area = GTK_DIALOG (dialog)->vbox; +#endif + + // add a scroll area + scrolledwindow = gtk_scrolled_window_new (NULL,NULL); + gtk_container_add (GTK_CONTAINER (content_area), scrolledwindow); + + // add a textview + view = gtk_text_view_new (); +// gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view), GTK_WRAP_WORD_CHAR); + gtk_text_view_set_editable (GTK_TEXT_VIEW (view), FALSE); + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); + gtk_text_buffer_set_text (buffer, msg, -1); + gtk_container_add (GTK_CONTAINER (scrolledwindow), view); + + // run the dialog + if (parent) { + gtk_window_get_size (GTK_WINDOW(parent), &width, &height); + gtk_window_set_default_size (GTK_WINDOW(dialog), width, height); + } + gtk_widget_show_all (dialog); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); +} diff --git a/src/plugins/bi_import/gui.h b/src/plugins/bi_import/gui.h new file mode 100644 index 0000000000..e94c535afd --- /dev/null +++ b/src/plugins/bi_import/gui.h @@ -0,0 +1,48 @@ +/* + * gui.h -- + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, contact: + * + * Free Software Foundation Voice: +1-617-542-5942 + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 + * Boston, MA 02110-1301, USA gnu@gnu.org + */ + +/** + * @addtogroup Tools + * @{ + * @file gui.h + * @brief GUI handling for bi import plugin + * @author Copyright (C) 2009 Sebastian Held + */ + +#ifndef GNC_PLUGIN_bi_import_gui_H +#define GNC_PLUGIN_bi_import_gui_H + +#include + +G_BEGIN_DECLS + +typedef struct _bi_import_gui BillImportGui; + +/** + * File chooser + */ +BillImportGui *gnc_plugin_bi_import_showGUI(void); + +G_END_DECLS + +#endif /* GNC_PLUGIN_bi_import_gui_H */ + +/** @} */ diff --git a/src/plugins/bi_import/helpers.c b/src/plugins/bi_import/helpers.c new file mode 100644 index 0000000000..9de7940397 --- /dev/null +++ b/src/plugins/bi_import/helpers.c @@ -0,0 +1,80 @@ +/** + * helpers.c -- + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, contact: + * + * Free Software Foundation Voice: +1-617-542-5942 + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 + * Boston, MA 02110-1301, USA gnu@gnu.org + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "helpers.h" + +//! \brief helper function +gboolean text2bool( const gchar *text ) +{ + gboolean erg = FALSE; + gchar *temp; + + if (!text) + return erg; + + temp = g_strdup( text ); + g_strstrip( temp ); + if ((g_ascii_strcasecmp( temp, "yes" ) == 0) || (g_ascii_strcasecmp( temp, "true" ) == 0) || + (g_ascii_strcasecmp( temp, "1" ) == 0) || (g_ascii_strcasecmp( temp, "x" ) == 0)) + erg = TRUE; + g_free( temp ); + return erg; +} + +//! \brief helper function +GncAmountType text2disc_type( const gchar *text ) +{ + GncAmountType type = GNC_AMT_TYPE_PERCENT; + gchar *temp; + + if (!text) + return type; + + temp = g_strdup( text ); + g_strstrip( temp ); + if ((strlen(temp) > 0) && (g_ascii_strcasecmp( temp, "%" ) != 0)) + type = GNC_AMT_TYPE_VALUE; + g_free( temp ); + return type; +} + +//! \brief helper function +GncDiscountHow text2disc_how( const gchar *text ) +{ + GncDiscountHow how = GNC_DISC_PRETAX; + gchar *temp; + + if (!text) + return how; + + temp = g_strdup( text ); + g_strstrip( temp ); + if (g_ascii_strcasecmp( temp, "=" ) == 0) + how = GNC_DISC_SAMETIME; + else if (g_ascii_strcasecmp( temp, ">" ) == 0) + how = GNC_DISC_POSTTAX; + g_free( temp ); + return how; +} diff --git a/src/plugins/bi_import/helpers.h b/src/plugins/bi_import/helpers.h new file mode 100644 index 0000000000..87dccb2a3c --- /dev/null +++ b/src/plugins/bi_import/helpers.h @@ -0,0 +1,32 @@ +/** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, contact: + * + * Free Software Foundation Voice: +1-617-542-5942 + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 + * Boston, MA 02110-1301, USA gnu@gnu.org + * + * + */ + + + +#include +#include +#include +#include "gncEntry.h" + +gboolean text2bool( const gchar *text ); +GncAmountType text2disc_type( const gchar *text ); +GncDiscountHow text2disc_how( const gchar *text ); diff --git a/src/plugins/bi_import/regex.txt b/src/plugins/bi_import/regex.txt new file mode 100644 index 0000000000..3679664763 --- /dev/null +++ b/src/plugins/bi_import/regex.txt @@ -0,0 +1 @@ +^[#]*(?[^,]*),(?[^,]*),(?[^,]*),(?[^,]*),?(?[^,]*),?(?[^,]*),?(?[^,]*),?(?[^,]*),?(?[^,]*),?(?[^,]*),?(?[^,]*),?(?[^,]*),?(?[^,]*),?(?[^,]*),?(?[^,]*),?(?[^,]*),?(?[^,]*),(?[^,]*),(?[^,]*),(?[^,]*),(?[^,]*),(?[^,]*) diff --git a/src/plugins/bi_import/ui/Makefile.am b/src/plugins/bi_import/ui/Makefile.am new file mode 100644 index 0000000000..b06854c619 --- /dev/null +++ b/src/plugins/bi_import/ui/Makefile.am @@ -0,0 +1,5 @@ +uidir = $(GNC_UI_DIR) +ui_DATA = \ + gnc-plugin-bi_import-ui.xml + +EXTRA_DIST = $(ui_DATA) diff --git a/src/plugins/bi_import/ui/gnc-plugin-bi_import-ui.xml b/src/plugins/bi_import/ui/gnc-plugin-bi_import-ui.xml new file mode 100644 index 0000000000..f92f6b38e3 --- /dev/null +++ b/src/plugins/bi_import/ui/gnc-plugin-bi_import-ui.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + +