GnuCash 2.4.99
dialog-bi-import.c
00001 /*
00002  * dialog-bi-import.c -- Invoice importer Core functions
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of
00007  * the License, or (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, contact:
00016  *
00017  * Free Software Foundation           Voice:  +1-617-542-5942
00018  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
00019  * Boston, MA  02110-1301,  USA       gnu@gnu.org
00020  */
00021 
00033 #ifdef HAVE_CONFIG_H
00034 #include "config.h"
00035 #endif
00036 
00037 #ifndef HAVE_LOCALTIME_R
00038 #include "localtime_r.h"
00039 #endif
00040 
00041 #include <glib/gi18n.h>
00042 #include <regex.h>
00043 #include <glib.h>
00044 #include <glib/gstdio.h>
00045 
00046 #include "gnc-ui.h"
00047 #include "gnc-ui-util.h"
00048 #include "gnc-gui-query.h"
00049 #include "gncAddress.h"
00050 #include "gncVendorP.h"
00051 #include "gncVendor.h"
00052 #include "gncEntry.h"
00053 
00054 #include "gnc-exp-parser.h"
00055 
00056 // query
00057 #include "Query.h"
00058 #include "qof.h"
00059 #include "GNCId.h"
00060 #include "gncIDSearch.h"
00061 #include "dialog-bi-import.h"
00062 #include "dialog-bi-import-helper.h"
00063 
00064 // To open the invoices for editing
00065 #include "business/business-gnome/gnc-plugin-page-invoice.h"
00066 #include "business/business-gnome/dialog-invoice.h"
00067 
00068 
00069 //#ifdef HAVE_GLIB_2_14
00070 // glib >= 2.14.0
00071 // perl regular expressions are available
00072 
00073 // this helper macro takes a regexp match and fills the model
00074 #define FILL_IN_HELPER(match_name,column) \
00075             temp = g_match_info_fetch_named (match_info, match_name); \
00076             if (temp) \
00077             { \
00078                 g_strstrip( temp ); \
00079                 gtk_list_store_set (store, &iter, column, temp, -1); \
00080                 g_free (temp); \
00081             }
00082 
00083 
00084 bi_import_result
00085 gnc_bi_import_read_file (const gchar * filename, const gchar * parser_regexp,
00086                          GtkListStore * store, guint max_rows,
00087                          bi_import_stats * stats)
00088 {
00089     // some statistics
00090     bi_import_stats stats_fallback;
00091     FILE *f;
00092 
00093     // regexp
00094     char *line;
00095     gchar *line_utf8, *temp;
00096     GMatchInfo *match_info;
00097     GError *err;
00098     GRegex *regexpat;
00099 
00100     // model
00101     GtkTreeIter iter;
00102 
00103     f = g_fopen (filename, "rt");
00104     if (!f)
00105     {
00106         //gnc_error_dialog( 0, _("File %s cannot be opened."), filename );
00107         return RESULT_OPEN_FAILED;
00108     }
00109 
00110     // set up statistics
00111     if (!stats)
00112         stats = &stats_fallback;
00113 
00114     // compile the regular expression and check for errors
00115     err = NULL;
00116     regexpat =
00117         g_regex_new (parser_regexp, G_REGEX_EXTENDED | G_REGEX_OPTIMIZE | G_REGEX_DUPNAMES, 0, &err);
00118     if (err != NULL)
00119     {
00120         GtkWidget *dialog;
00121         gchar *errmsg;
00122 
00123         errmsg = g_strdup_printf (_("Error in regular expression '%s':\n%s"),
00124                                   parser_regexp, err->message);
00125         g_error_free (err);
00126         err = NULL;
00127 
00128         dialog = gtk_message_dialog_new (NULL,
00129                                          GTK_DIALOG_MODAL,
00130                                          GTK_MESSAGE_ERROR,
00131                                          GTK_BUTTONS_OK, "%s", errmsg);
00132         gtk_dialog_run (GTK_DIALOG (dialog));
00133         gtk_widget_destroy (dialog);
00134         g_free (errmsg);
00135         errmsg = 0;
00136 
00137         fclose (f);
00138         return RESULT_ERROR_IN_REGEXP;
00139     }
00140 
00141     // start the import
00142     stats->n_imported = 0;
00143     stats->n_ignored = 0;
00144     stats->ignored_lines = g_string_new (NULL);
00145 #define buffer_size 1000
00146     line = g_malloc0 (buffer_size);
00147     while (!feof (f)
00148             && ((max_rows == 0)
00149                 || (stats->n_imported + stats->n_ignored < max_rows)))
00150     {
00151         int l;
00152         // read one line
00153         if (!fgets (line, buffer_size, f))
00154             break;                      // eof
00155         // now strip the '\n' from the end of the line
00156         l = strlen (line);
00157         if ((l > 0) && (line[l - 1] == '\n'))
00158             line[l - 1] = 0;
00159 
00160         // convert line from locale into utf8
00161         line_utf8 = g_locale_to_utf8 (line, -1, NULL, NULL, NULL);
00162 
00163         // parse the line
00164         match_info = NULL;      // it seems, that in contrast to documentation, match_info is not alsways set -> g_match_info_free will segfault
00165         if (g_regex_match (regexpat, line_utf8, 0, &match_info))
00166         {
00167             // match found
00168             stats->n_imported++;
00169 
00170             // fill in the values
00171             gtk_list_store_append (store, &iter);
00172             FILL_IN_HELPER ("id", ID); /* FIXME: Should "id" be translated? I don't think so. */
00173             FILL_IN_HELPER ("date_opened", DATE_OPENED);
00174             FILL_IN_HELPER ("owner_id", OWNER_ID);
00175             FILL_IN_HELPER ("billing_id", BILLING_ID);
00176             FILL_IN_HELPER ("notes", NOTES);
00177 
00178             FILL_IN_HELPER ("date", DATE);
00179             FILL_IN_HELPER ("desc", DESC);
00180             FILL_IN_HELPER ("action", ACTION);
00181             FILL_IN_HELPER ("account", ACCOUNT);
00182             FILL_IN_HELPER ("quantity", QUANTITY);
00183             FILL_IN_HELPER ("price", PRICE);
00184             FILL_IN_HELPER ("disc_type", DISC_TYPE);
00185             FILL_IN_HELPER ("disc_how", DISC_HOW);
00186             FILL_IN_HELPER ("discount", DISCOUNT);
00187             FILL_IN_HELPER ("taxable", TAXABLE);
00188             FILL_IN_HELPER ("taxincluded", TAXINCLUDED);
00189             FILL_IN_HELPER ("tax_table", TAX_TABLE);
00190 
00191             FILL_IN_HELPER ("date_posted", DATE_POSTED);
00192             FILL_IN_HELPER ("due_date", DUE_DATE);
00193             FILL_IN_HELPER ("account_posted", ACCOUNT_POSTED);
00194             FILL_IN_HELPER ("memo_posted", MEMO_POSTED);
00195             FILL_IN_HELPER ("accu_splits", ACCU_SPLITS);
00196         }
00197         else
00198         {
00199             // ignore line
00200             stats->n_ignored++;
00201             g_string_append (stats->ignored_lines, line_utf8);
00202             g_string_append_c (stats->ignored_lines, '\n');
00203         }
00204 
00205         g_match_info_free (match_info);
00206         match_info = 0;
00207         g_free (line_utf8);
00208         line_utf8 = 0;
00209     }
00210     g_free (line);
00211     line = 0;
00212 
00213     g_regex_unref (regexpat);
00214     regexpat = 0;
00215     fclose (f);
00216 
00217     if (stats == &stats_fallback)
00218         // stats are not requested -> free the string
00219         g_string_free (stats->ignored_lines, TRUE);
00220 
00221     return RESULT_OK;
00222 }
00223 
00224 
00231 void
00232 gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
00233                        GString * info, gchar *type)
00234 {
00235     GtkTreeIter iter;
00236     gboolean valid, row_deleted, row_fixed;
00237     gchar *id, *date_opened, *date_posted, *owner_id, *date, *quantity, *price;
00238     GString *prev_id, *prev_date_opened, *prev_date_posted, *prev_owner_id, *prev_date; // needed to fix multi line invoices
00239     guint dummy;
00240     gint row = 1;
00241 
00242     // allow the call to this function with only GtkListeStore* specified
00243     if (!fixed)
00244         fixed = &dummy;
00245     if (!deleted)
00246         deleted = &dummy;
00247 
00248     *fixed = 0;
00249     *deleted = 0;
00250 
00251     // init strings
00252     prev_id = g_string_new ("");
00253     prev_date_opened = g_string_new ("");
00254     prev_date_posted = g_string_new ("");
00255     prev_owner_id = g_string_new ("");
00256     prev_date = g_string_new ("");
00257 
00258     valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter);
00259     while (valid)
00260     {
00261         row_deleted = FALSE;
00262         row_fixed = FALSE;
00263 
00264         // Walk through the list, reading each row
00265         gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
00266                             ID, &id,
00267                             DATE_OPENED, &date_opened,
00268                             DATE_POSTED, &date_posted,
00269                             OWNER_ID, &owner_id,
00270                             DATE, &date,
00271                             QUANTITY, &quantity, PRICE, &price, -1);
00272 
00273         if (strlen (price) == 0)
00274         {
00275             // invalid row (no price given)
00276             // no fix possible -> delete row
00277             valid = gtk_list_store_remove (store, &iter);
00278             row_deleted = TRUE;
00279             g_string_append_printf (info,
00280                                     _("ROW %d DELETED, PRICE_NOT_SET: id=%s\n"),
00281                                     row, id);
00282         }
00283         else if (strlen (quantity) == 0)
00284         {
00285             // invalid row (no quantity given)
00286             // no fix possible -> delete row
00287             valid = gtk_list_store_remove (store, &iter);
00288             row_deleted = TRUE;
00289             g_string_append_printf (info, _("ROW %d DELETED, QTY_NOT_SET: id=%s\n"),
00290                                     row, id);
00291         }
00292         else
00293         {
00294             if (strlen (id) == 0)
00295             {
00296                 // no invoice id specified
00297                 if (prev_id->len == 0)
00298                 {
00299                     // cannot fix -> delete row
00300                     valid = gtk_list_store_remove (store, &iter);
00301                     row_deleted = TRUE;
00302                     g_string_append_printf (info,
00303                                             _("ROW %d DELETED, ID_NOT_SET\n"), row);
00304                 }
00305                 else
00306                 {
00307                     // this is a fixable multi line invoice
00308                     gtk_list_store_set (store, &iter, ID, prev_id->str, -1);
00309                     row_fixed = TRUE;
00310                 }
00311             }
00312             else
00313             {
00314                 // remember invoice id (to be able to fix multi line invoices)
00315                 g_string_assign (prev_id, id);
00316                 // new invoice => reset all other fixable entries
00317                 g_string_assign (prev_date_opened, "");
00318                 g_string_assign (prev_date_posted, "");
00319                 g_string_assign (prev_owner_id, "");
00320                 g_string_assign (prev_date, "");
00321             }
00322         }
00323 
00324         if (!row_deleted)
00325         {
00326             // the row is valid (price and id are valid)
00327 
00328             if (strlen (date_opened) == 0)
00329             {
00330                 if (prev_date_opened->len == 0)
00331                 {
00332                     // fix this by using the current date (why is this so complicated?)
00333                     gchar temp[20];
00334                     GDate *date;
00335                     time_t secs;
00336                     struct tm now;
00337                     time (&secs);
00338                     localtime_r (&secs, &now);
00339                     date =
00340                         g_date_new_dmy (now.tm_mday, now.tm_mon + 1,
00341                                         now.tm_year + 1900);
00342                     g_date_strftime (temp, 20, "%x", date);     // create a locale specific date string
00343                     g_string_assign (prev_date_opened, temp);
00344                     g_date_free (date);
00345                 }
00346                 // fix this by using the previous date_opened value (multi line invoice)
00347                 gtk_list_store_set (store, &iter, DATE_OPENED,
00348                                     prev_date_opened->str, -1);
00349                 row_fixed = TRUE;
00350             }
00351             else
00352             {
00353                 // remember date_opened (to be able to fix multi line invoices)
00354                 g_string_assign (prev_date_opened, date_opened);
00355             }
00356 
00357             // date_opened is valid
00358 
00359             if (strlen (date_posted) == 0)
00360             {
00361                 if (prev_date_posted->len == 0)
00362                 {
00363                     // this invoice will have to get posted manually
00364                 }
00365                 else
00366                 {
00367                     // multi line invoice => fix it
00368                     gtk_list_store_set (store, &iter, DATE_POSTED,
00369                                         prev_date_posted->str, -1);
00370                     row_fixed = TRUE;
00371                 }
00372             }
00373             else
00374             {
00375                 // remember date_opened (to be able to fix multi line invoices)
00376                 g_string_assign (prev_date_posted, date_posted);
00377             }
00378 
00379             // date_posted is valid
00380 
00381             if (strlen (quantity) == 0)
00382             {
00383                 // quantity is unset => set to 1
00384                 gtk_list_store_set (store, &iter, QUANTITY, "1", -1);
00385                 row_fixed = TRUE;
00386             }
00387 
00388             // quantity is valid
00389 
00390             if (strlen (owner_id) == 0)
00391             {
00392                 if (prev_owner_id->len == 0)
00393                 {
00394                     // no customer given and not fixable => delete row
00395                     valid = gtk_list_store_remove (store, &iter);
00396                     row_deleted = TRUE;
00397                     g_string_append_printf (info,
00398                                             _("ROW %d DELETED, OWNER_NOT_SET: id=%s\n"),
00399                                             row, id);
00400                 }
00401                 else
00402                 {
00403                     gtk_list_store_set (store, &iter, owner_id,
00404                                         prev_owner_id->str, -1);
00405                     row_fixed = TRUE;
00406                 }
00407             }
00408             else
00409             {
00410                 // remember owner_id
00411                 g_string_assign (prev_owner_id, owner_id);
00412             }
00413             if (g_ascii_strcasecmp (type, "BILL") == 0)
00414             {
00415                 // BILL: check, if vendor exists
00416                 if (!gnc_search_vendor_on_id
00417                         (gnc_get_current_book (), prev_owner_id->str))
00418                 {
00419                     // vendor not found => delete row
00420                     valid = gtk_list_store_remove (store, &iter);
00421                     row_deleted = TRUE;
00422                     g_string_append_printf (info,
00423                                             _("ROW %d DELETED, VENDOR_DOES_NOT_EXIST: id=%s\n"),
00424                                             row, id);
00425                 }
00426             }
00427             else if (g_ascii_strcasecmp (type, "INVOICE") == 0)
00428             {
00429                 // INVOICE: check, if customer exists
00430                 if (!gnc_search_customer_on_id
00431                         (gnc_get_current_book (), prev_owner_id->str))
00432                 {
00433                     // customer not found => delete row
00434                     valid = gtk_list_store_remove (store, &iter);
00435                     row_deleted = TRUE;
00436                     g_string_append_printf (info,
00437                                             _("ROW %d DELETED, CUSTOMER_DOES_NOT_EXIST: id=%s\n"),
00438                                             row, id);
00439                 }
00440             }
00441 
00442             // owner_id is valid
00443         }
00444 
00445         g_free (id);
00446         g_free (date_opened);
00447         g_free (date_posted);
00448         g_free (owner_id);
00449         g_free (date);
00450         g_free (quantity);
00451         g_free (price);
00452         if (row_deleted)
00453         {
00454             (*deleted)++;
00455             // reset all remembered values
00456             g_string_assign (prev_id, "");
00457             g_string_assign (prev_date_opened, "");
00458             g_string_assign (prev_date_posted, "");
00459             g_string_assign (prev_owner_id, "");
00460             g_string_assign (prev_date, "");
00461         }
00462         else if (row_fixed)
00463             (*fixed)++;
00464 
00465         if (!row_deleted)
00466             valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
00467 
00468         row++;
00469     }
00470 
00471     // deallocate strings
00472     g_string_free (prev_id, TRUE);
00473     g_string_free (prev_date_opened, TRUE);
00474     g_string_free (prev_date_posted, TRUE);
00475     g_string_free (prev_owner_id, TRUE);
00476     g_string_free (prev_date, TRUE);
00477 
00478     if (info && (info->len > 0))
00479     {
00480         g_string_prepend (info, "\n\n");
00481         g_string_prepend (info, _("These rows were deleted:"));
00482     }
00483 }
00484 
00485 
00486 /***********************************************************************
00487  * @todo Maybe invoice checking should be done in gnc_bi_import_fix_bis (...)
00488  * rather than in here?  But that is more concerned with ensuring the csv is consistent.
00489  * @param GtkListStore *store
00490  * @param guint *n_invoices_created
00491  * @param guint *n_invoices_updated
00492  * @return void
00493  ***********************************************************************/
00494 void
00495 gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
00496                           guint * n_invoices_created,
00497                           guint * n_invoices_updated,
00498                           gchar * type, gchar * open_mode )
00499 {
00500     gboolean valid;
00501     GtkTreeIter iter;
00502     gchar *id, *date_opened, *owner_id, *billing_id, *notes;
00503     gchar *date, *desc, *action, *account, *quantity, *price, *disc_type,
00504           *disc_how, *discount, *taxable, *taxincluded, *tax_table;
00505     gchar *date_posted, *due_date, *account_posted, *memo_posted,
00506           *accumulatesplits;
00507     guint dummy;
00508     GncInvoice *invoice;
00509     GncOrder *order;
00510     GncEntry *entry;
00511     gint day, month, year;
00512     gnc_numeric n;
00513     GncOwner *owner;
00514     Account *acc;
00515     enum update {YES = GTK_RESPONSE_YES, NO = GTK_RESPONSE_NO} update;
00516     GtkWidget *dialog;
00517     Timespec today;
00518     GncPluginPage *new_page;
00519     InvoiceWindow *iw;
00520 
00521     // these arguments are needed
00522     g_return_if_fail (store && book);
00523 
00524     // allow to call this function without statistics
00525     if (!n_invoices_created)
00526         n_invoices_created = &dummy;
00527     if (!n_invoices_updated)
00528         n_invoices_updated = &dummy;
00529     *n_invoices_created = 0;
00530     *n_invoices_updated = 0;
00531 
00532     invoice = NULL;
00533     order = NULL;
00534     update = NO;
00535 
00536     valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter);
00537     while (valid)
00538     {
00539         // Walk through the list, reading each row
00540         gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ID, &id, DATE_OPENED, &date_opened, DATE_POSTED, &date_posted,       // if autoposting requested
00541                             DUE_DATE, &due_date,        // if autoposting requested
00542                             ACCOUNT_POSTED, &account_posted,    // if autoposting requested
00543                             MEMO_POSTED, &memo_posted,  // if autoposting requested
00544                             ACCU_SPLITS, &accumulatesplits,     // if autoposting requested
00545                             OWNER_ID, &owner_id,
00546                             BILLING_ID, &billing_id,
00547                             NOTES, &notes,
00548                             DATE, &date,
00549                             DESC, &desc,
00550                             ACTION, &action,
00551                             ACCOUNT, &account,
00552                             QUANTITY, &quantity,
00553                             PRICE, &price,
00554                             DISC_TYPE, &disc_type,
00555                             DISC_HOW, &disc_how,
00556                             DISCOUNT, &discount,
00557                             TAXABLE, &taxable,
00558                             TAXINCLUDED, &taxincluded,
00559                             TAX_TABLE, &tax_table, -1);
00560 
00561         // TODO:  Assign a new invoice number if one is absent.  BUT we don't want to assign a new invoice for every line!!
00562         // so we'd have to flag this up somehow or add an option in the import GUI.  The former implies that we make
00563         // an assumption about what the importer (person) wants to do.  It seems resonable that a CSV file full of items with
00564         // If an invoice exists then we add to it in this current schema.
00565         // no predefined invoice number is a new invoice that's in need of a new number.
00566         // This was  not designed to satisfy the need for repeat invoices however, so maybe we need a another method for this, after all
00567         // It should be easier to copy an invoice with a new ID than to go through all this malarky.
00568         if (g_ascii_strcasecmp (type, "BILL") == 0)
00569             invoice = gnc_search_bill_on_id (book, id);
00570         else if (g_ascii_strcasecmp (type, "INVOICE") == 0)
00571             invoice = gnc_search_invoice_on_id (book, id);
00572 
00573         if (!invoice)
00574         {
00575             // new invoice
00576             invoice = gncInvoiceCreate (book);
00577             gncInvoiceSetID (invoice, id);
00578             owner = gncOwnerNew ();
00579             if (g_ascii_strcasecmp (type, "BILL") == 0)
00580                 gncOwnerInitVendor (owner,
00581                                     gnc_search_vendor_on_id (book, owner_id));
00582             else if (g_ascii_strcasecmp (type, "INVOICE") == 0)
00583                 gncOwnerInitCustomer (owner,
00584                                       gnc_search_customer_on_id (book, owner_id));
00585             gncInvoiceSetOwner (invoice, owner);
00586             gncInvoiceSetCurrency (invoice, gncOwnerGetCurrency (owner));       // Set the invoice currency based on the owner
00587             if (strlen (date_opened) != 0)      // If a date is specified in CSV
00588             {
00589                 qof_scan_date (date_opened, &day, &month, &year);
00590                 gncInvoiceSetDateOpened (invoice,
00591                                          gnc_dmy2timespec (day, month, year));
00592             }
00593             else                        // If no date in CSV
00594             {
00595                 time_t now = time (NULL);
00596                 Timespec now_timespec;
00597                 timespecFromTime_t (&now_timespec, now);
00598                 gncInvoiceSetDateOpened (invoice, now_timespec);
00599             }
00600             gncInvoiceSetBillingID (invoice, billing_id ? billing_id : "");
00601             gncInvoiceSetNotes (invoice, notes ? notes : "");
00602             gncInvoiceSetActive (invoice, TRUE);
00603             //if (g_ascii_strcasecmp(type,"INVOICE"))gncInvoiceSetBillTo( invoice, billto );
00604             (*n_invoices_created)++;
00605             update = YES;
00606 
00607             // open new bill / invoice in a tab, if requested
00608             if (g_ascii_strcasecmp(open_mode, "ALL") == 0
00609                     || (g_ascii_strcasecmp(open_mode, "NOT_POSTED") == 0
00610                         && strlen(date_posted) == 0))
00611             {
00612                 iw =  gnc_ui_invoice_edit (invoice);
00613                 new_page = gnc_plugin_page_invoice_new (iw);
00614             }
00615         }
00616 // I want to warn the user that an existing billvoice exists, but not every
00617 // time.
00618 // An import can contain many lines usually referring to the same invoice.
00619 // NB: Posted invoices are NEVER updated.
00620         else                    // if invoice exists
00621         {
00622             if (gncInvoiceIsPosted (invoice))   // Is it already posted?
00623             {
00624                 valid =
00625                     gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
00626                 continue;               // If already posted then never import
00627             }
00628             if (update != YES)  // Pop up a dialog to ask if updates are the expected action
00629             {
00630                 dialog = gtk_message_dialog_new (NULL,
00631                                                  GTK_DIALOG_MODAL,
00632                                                  GTK_MESSAGE_ERROR,
00633                                                  GTK_BUTTONS_YES_NO,
00634                                                  "%s",
00635                                                  _("Are you sure you have bills/invoices to update?"));
00636                 update = gtk_dialog_run (GTK_DIALOG (dialog));
00637                 gtk_widget_destroy (dialog);
00638                 if (update == NO)
00639                 {
00640                     // Cleanup and leave
00641                     g_free (id);
00642                     g_free (date_opened);
00643                     g_free (owner_id);
00644                     g_free (billing_id);
00645                     g_free (notes);
00646                     g_free (date);
00647                     g_free (desc);
00648                     g_free (action);
00649                     g_free (account);
00650                     g_free (quantity);
00651                     g_free (price);
00652                     g_free (disc_type);
00653                     g_free (disc_how);
00654                     g_free (discount);
00655                     g_free (taxable);
00656                     g_free (taxincluded);
00657                     g_free (tax_table);
00658                     g_free (date_posted);
00659                     g_free (due_date);
00660                     g_free (account_posted);
00661                     g_free (memo_posted);
00662                     g_free (accumulatesplits);
00663                     return;
00664                 }
00665             }
00666             (*n_invoices_updated)++;
00667         }
00668 
00669 
00670         // add entry to invoice/bill
00671         entry = gncEntryCreate (book);
00672         qof_scan_date (date, &day, &month, &year);
00673         {
00674             GDate *date = g_date_new_dmy(day, month, year);
00675             gncEntrySetDateGDate (entry, date);
00676             g_date_free (date);
00677         }
00678         timespecFromTime_t (&today, time (NULL));       // set today to the current date
00679         gncEntrySetDateEntered (entry, today);
00680         gncEntrySetDescription (entry, desc);
00681         gncEntrySetAction (entry, action);
00682 
00683         n = gnc_numeric_zero ();
00684         gnc_exp_parser_parse (quantity, &n, NULL);
00685         gncEntrySetQuantity (entry, n);
00686         acc = gnc_account_lookup_for_register (gnc_get_current_root_account (),
00687                                                account);
00688         if (g_ascii_strcasecmp (type, "BILL") == 0)
00689         {
00690             gncEntrySetBillAccount (entry, acc);
00691             n = gnc_numeric_zero ();
00692             gnc_exp_parser_parse (price, &n, NULL);
00693             gncEntrySetBillPrice (entry, n);
00694             gncEntrySetBillTaxable (entry, text2bool (taxable));
00695             gncEntrySetBillTaxIncluded (entry, text2bool (taxincluded));
00696             gncEntrySetBillTaxTable (entry,
00697                                      gncTaxTableLookupByName (book, tax_table));
00698             n = gnc_numeric_zero ();
00699             gnc_exp_parser_parse (discount, &n, NULL);
00700             gncBillAddEntry (invoice, entry);
00701         }
00702         else if (g_ascii_strcasecmp (type, "INVOICE") == 0)
00703         {
00704             gncEntrySetNotes (entry, notes);
00705             gncEntrySetInvAccount (entry, acc);
00706             n = gnc_numeric_zero ();
00707             gnc_exp_parser_parse (price, &n, NULL);
00708             gncEntrySetInvPrice (entry, n);
00709             gncEntrySetInvTaxable (entry, text2bool (taxable));
00710             gncEntrySetInvTaxIncluded (entry, text2bool (taxincluded));
00711             gncEntrySetInvTaxTable (entry,
00712                                     gncTaxTableLookupByName (book, tax_table));
00713             n = gnc_numeric_zero ();
00714             gnc_exp_parser_parse (discount, &n, NULL);
00715             gncEntrySetInvDiscount (entry, n);
00716             gncEntrySetInvDiscountType (entry, text2disc_type (disc_type));
00717             gncEntrySetInvDiscountHow (entry, text2disc_how (disc_how));
00718             gncInvoiceAddEntry (invoice, entry);
00719         }
00720         valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
00721 
00722         // handle auto posting of invoices
00723         {
00724             gchar *new_id = NULL;
00725             Transaction *tnx;
00726             if (valid)
00727                 gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ID, &new_id, -1);
00728             if (g_strcmp0 (id, new_id) != 0)
00729             {
00730                 // the next invoice id is different => try to autopost this invoice
00731                 if (qof_scan_date (date_posted, &day, &month, &year))
00732                 {
00733                     // autopost this invoice
00734                     Timespec d1, d2;
00735                     d1 = gnc_dmy2timespec (day, month, year);
00736                     qof_scan_date (due_date, &day, &month, &year);      // obtains the due date, or leaves it at date_posted
00737                     d2 = gnc_dmy2timespec (day, month, year);
00738                     acc = gnc_account_lookup_for_register
00739                           (gnc_get_current_root_account (), account_posted);
00740                     tnx = gncInvoicePostToAccount (invoice, acc, &d1, &d2,
00741                                                    memo_posted,
00742                                                    text2bool (accumulatesplits));
00743                 }
00744             }
00745             g_free (new_id);
00746         }
00747 
00748         // cleanup
00749         g_free (id);
00750         g_free (date_opened);
00751         g_free (owner_id);
00752         g_free (billing_id);
00753         g_free (notes);
00754         g_free (date);
00755         g_free (desc);
00756         g_free (action);
00757         g_free (account);
00758         g_free (quantity);
00759         g_free (price);
00760         g_free (disc_type);
00761         g_free (disc_how);
00762         g_free (discount);
00763         g_free (taxable);
00764         g_free (taxincluded);
00765         g_free (tax_table);
00766         g_free (date_posted);
00767         g_free (due_date);
00768         g_free (account_posted);
00769         g_free (memo_posted);
00770         g_free (accumulatesplits);
00771     }
00772 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines