gnucash maint: Multiple changes pushed

Geert Janssens gjanssens at code.gnucash.org
Thu Jan 23 12:55:40 EST 2020


Updated	 via  https://github.com/Gnucash/gnucash/commit/a033b7b1 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/fefe427b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/13add9c4 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/0423fd72 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/8c31b1c9 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/66950e63 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/53aec754 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/75d45653 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/94a4d762 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/91f68e94 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/ffd92534 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/31f23404 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/eab9f19b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/57a554d6 (commit)
	from  https://github.com/Gnucash/gnucash/commit/ad612c48 (commit)



commit a033b7b1e94f7e7d5b20c39f9f0ffd0864119f91
Merge: ad612c482 fefe427bf
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Thu Jan 23 18:50:09 2020 +0100

    Merge branch 'bugs_ibi' of https://github.com/ekinonnakapito/gnucash into maint


commit fefe427bf15c76966be69d51ccc56683227c8fd2
Author: Rob Laan <rob.laan at chello.nl>
Date:   Sun Jul 7 13:28:12 2019 +0200

    Import Bills & Invoices: Report all errors instead of just the first error of an invoice.

diff --git a/gnucash/import-export/bi-import/dialog-bi-import.c b/gnucash/import-export/bi-import/dialog-bi-import.c
index 3c97f4b02..d6b3fd610 100644
--- a/gnucash/import-export/bi-import/dialog-bi-import.c
+++ b/gnucash/import-export/bi-import/dialog-bi-import.c
@@ -246,7 +246,7 @@ gnc_bi_import_read_file (const gchar * filename, const gchar * parser_regexp,
  
  Validates the import data; any error causes all rows of the same invoice
  to be deleted from the import data:
- - id is not set
+ - id is not set, and there is no previous id
  - owner_id is not set, or customer/vendor does not exist
  - date_posted is not valid
  - account_posted does not exist
@@ -277,7 +277,7 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * n_rows_fixed, guint * n_row
     GString *running_id;
     Account *acc = NULL;
     guint dummy;
-    gint row = 1, fixed_for_invoice = 0;
+    gint row = 1, fixed_for_invoice = 0, invoice_line = 0;
     const gchar* date_format_string = qof_date_format_get_string (qof_date_format_get()); // Get the user set date format string
 
     DEBUG("date_format_string: %s",date_format_string);
@@ -301,6 +301,7 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * n_rows_fixed, guint * n_row
     valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter);
     while (valid)
     {
+        ++invoice_line;
         row_fixed = FALSE;
 
         //  If this is a row for a new invoice id, validate header values.
@@ -320,112 +321,118 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * n_rows_fixed, guint * n_row
             // Validate the invoice id.
             if (strlen (id) == 0)
             {
-                // The id in the first row of an invoice is blank, ignore the invoice
+                // If there was an earlier valid id, then it replaces an empty id when the next row is read at the end of the loop.
+                // So an empty id error can only happen on the first row of an import file.
                 ignore_invoice = TRUE;
                 g_string_append_printf (info,
-                                        _("Row %d: invoice ignored, invoice ID not set.\n"), row);
+                                        _("Row %d: no invoice ID in first row of import file.\n"), row);
             }
 
             // Validate customer or vendor.
-            if (!ignore_invoice && strlen (owner_id) == 0)
+            if (strlen (owner_id) == 0)
             {
                 ignore_invoice = TRUE;
                 g_string_append_printf (info,
-                                        _("Row %d: invoice %s ignored, owner not set.\n"),
-                                        row, id);
+                                        _("Row %d, invoice %s/%u: owner not set.\n"),
+                                        row, id, invoice_line);
             }
             // Verify that customer or vendor exists.
-            if (!ignore_invoice)
+            if (g_ascii_strcasecmp (type, "BILL") == 0)
             {
-                if (g_ascii_strcasecmp (type, "BILL") == 0)
+                if (!gnc_search_vendor_on_id
+                    (gnc_get_current_book (), owner_id))
                 {
-                    if (!gnc_search_vendor_on_id
-                        (gnc_get_current_book (), owner_id))
-                    {
-                        // Vendor not found.
-                        ignore_invoice = TRUE;
-                        g_string_append_printf (info,
-                                                _("Row %d: invoice %s ignored, vendor %s does not exist.\n"),
-                                                row, id, owner_id);
-                    }
+                    // Vendor not found.
+                    ignore_invoice = TRUE;
+                    g_string_append_printf (info,
+                                            _("Row %d, invoice %s/%u: vendor %s does not exist.\n"),
+                                            row, id, invoice_line, owner_id);
                 }
-                else if (g_ascii_strcasecmp (type, "INVOICE") == 0)
+            }
+            else if (g_ascii_strcasecmp (type, "INVOICE") == 0)
+            {
+                if (!gnc_search_customer_on_id
+                    (gnc_get_current_book (), owner_id))
                 {
-                    if (!gnc_search_customer_on_id
-                        (gnc_get_current_book (), owner_id))
-                    {
-                        // Customer not found.
-                        ignore_invoice = TRUE;
-                        g_string_append_printf (info,
-                                                _("Row %d: invoice %s ignored, customer %s does not exist.\n"),
-                                                row, id, owner_id);
-                    }
+                    // Customer not found.
+                    ignore_invoice = TRUE;
+                    g_string_append_printf (info,
+                                            _("Row %d, invoice %s/%u: customer %s does not exist.\n"),
+                                            row, id, invoice_line, owner_id);
                 }
             }
             
-            if (!ignore_invoice && strlen(date_posted) != 0)
+            if (strlen(date_posted) != 0)
             {
-                // Validate the date posted.
+                // Validate the date posted and due date.
                 if (!isDateValid(date_posted))
                 {
                     // Invalid date posted in first row of invoice, ignore the invoice
                     ignore_invoice = TRUE;
                     g_string_append_printf (info,
-                                            _("Row %d: invoice %s ignored, %s is not a valid posting date.\n"),
-                                            row, id, date_posted);
+                                            _("Row %d, invoice %s/%u: %s is not a valid posting date.\n"),
+                                            row, id, invoice_line, date_posted);
+                    
+                    // Verify the due date.
+                    if (!isDateValid(due_date))
+                    {
+                        // Invalid due date in first row of invoice, without valid posting date to substitute.
+                        g_string_append_printf (info,
+                                                _("Row %d, invoice %s/%u: %s is not a valid due date.\n"),
+                                                row, id, invoice_line, due_date);
+                    }
                 }
-                
-                // Validate account posted.
-                // Account should exists, and should be of type A/R for invoices, A/P for bills.
-                if (!ignore_invoice)
+                else
                 {
-                    acc = gnc_account_lookup_for_register
-                    (gnc_get_current_root_account (), account_posted);
-                    if (acc == NULL)
+                    // Verify the due date.
+                    if (!isDateValid(due_date))
                     {
-                        ignore_invoice = TRUE;
-                        g_string_append_printf (info,
-                                                _("Row %d: invoice %s ignored, account %s does not exist.\n"),
-                                                row, id,account_posted);
+                        // Fix this by using the date posted.
+                        gtk_list_store_set (store, &iter, DUE_DATE,
+                                            date_posted, -1);
+                        row_fixed = TRUE;
                     }
-                    else
+                }
+                
+            // Validate account posted.
+            // Account should exists, and should be of type A/R for invoices, A/P for bills.
+                 acc = gnc_account_lookup_for_register
+                (gnc_get_current_root_account (), account_posted);
+                if (acc == NULL)
+                {
+                    ignore_invoice = TRUE;
+                    g_string_append_printf (info,
+                                            _("Row %d, invoice %s/%u: account %s does not exist.\n"),
+                                            row, id, invoice_line, account_posted);
+                }
+                else
+                {
+                    if (g_ascii_strcasecmp (type, "BILL") == 0)
                     {
-                        if (g_ascii_strcasecmp (type, "BILL") == 0)
+                        
+                        if (xaccAccountGetType (acc) != ACCT_TYPE_PAYABLE)
                         {
-                            
-                            if (xaccAccountGetType (acc) != ACCT_TYPE_PAYABLE)
-                            {
-                                ignore_invoice = TRUE;
-                                g_string_append_printf (info,
-                                                        _("Row %d: invoice %s ignored, account %s is not of type Accounts Payable.\n"),
-                                                        row, id, account_posted);
-                            }
+                            ignore_invoice = TRUE;
+                            g_string_append_printf (info,
+                                                    _("Row %d, invoice %s/%u: account %s is not of type Accounts Payable.\n"),
+                                                    row, id, invoice_line, account_posted);
                         }
-                        else if (g_ascii_strcasecmp (type, "INVOICE") == 0)
+                    }
+                    else if (g_ascii_strcasecmp (type, "INVOICE") == 0)
+                    {
+                        if (xaccAccountGetType (acc) != ACCT_TYPE_RECEIVABLE)
                         {
-                            if (xaccAccountGetType (acc) != ACCT_TYPE_RECEIVABLE)
-                            {
-                                ignore_invoice = TRUE;
-                                g_string_append_printf (info,
-                                                        _("Row %d: invoice %s ignored, account %s is not of type Accounts Receivable.\n"),
-                                                        row, id, account_posted);
-                            }
+                            ignore_invoice = TRUE;
+                            g_string_append_printf (info,
+                                                    _("Row %d, invoice %s/%u: account %s is not of type Accounts Receivable.\n"),
+                                                    row, id, invoice_line, account_posted);
                         }
                     }
                 }
-                
-                // Verify the due date.
-                if (!ignore_invoice && !isDateValid(due_date))
-                {
-                    // Fix this by using the date posted.
-                    gtk_list_store_set (store, &iter, DUE_DATE,
-                                        date_posted, -1);
-                    row_fixed = TRUE;
-                }
             }
             
             // Verify the date opened.
-            if(!ignore_invoice && !isDateValid(date_opened))
+            if(!isDateValid(date_opened))
             {
                 // Fix this by using the current date.
                 gchar temp[20];
@@ -439,7 +446,7 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * n_rows_fixed, guint * n_row
             }
         }
         
-        // Validate and fix item date for each row.
+        // Validate and fix item data for each row.
         
         // Get item data.
         gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
@@ -455,22 +462,19 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * n_rows_fixed, guint * n_row
             // No valid price, delete the row
             ignore_invoice = TRUE;
             g_string_append_printf (info,
-                                    _("Row %d: invoice %s ignored, price not set.\n"),
-                                    row, id);
+                                    _("Row %d, invoice %s/%u: price not set.\n"),
+                                    row, id, invoice_line);
         }
 
         // Validate the account
-        if (!ignore_invoice)
+        acc = gnc_account_lookup_for_register (gnc_get_current_root_account (),
+                                               account);
+        if (acc == NULL)
         {
-            acc = gnc_account_lookup_for_register (gnc_get_current_root_account (),
-                                                   account);
-            if (acc == NULL)
-            {
-                ignore_invoice = TRUE;
-                g_string_append_printf (info,
-                                        _("Row %d: invoice %s ignored, account %s does not exist.\n"),
-                                        row, id,account);
-            }
+            ignore_invoice = TRUE;
+            g_string_append_printf (info,
+                                    _("Row %d, invoice %s/%u: account %s does not exist.\n"),
+                                    row, id, invoice_line, account);
         }
         
         // Fix item data.
@@ -497,29 +501,10 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * n_rows_fixed, guint * n_row
         }
         if (row_fixed) ++fixed_for_invoice;
         
-        // Move to the next row, skipping all rows of the current invoice if it has a row with an error.
-        if (ignore_invoice)
-        {
-            // Skip all rows of the current invoice.
-            // Get the next row and its id.
-            iter = first_row_of_invoice;
-            while (valid && g_strcmp0 (id, running_id->str) == 0)
-            {
-                (*n_rows_ignored)++;
-                valid = gtk_list_store_remove (store, &iter);
-                if (valid) gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ID, &id, -1);
-            }
-            // Fixes for ignored invoices don't count in the statistics.
-            fixed_for_invoice = 0;
-            
-            ignore_invoice = FALSE;
-        }
-        else
-        {
-            // Get the next row and its id.
-            valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
-            if (valid) gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ID, &id, -1);
-        }
+        // Get the next row and its id.
+        valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
+        if (valid) gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ID, &id, -1);
+
         
         // If the id of the next row is blank, it takes the id of the previous row.
         if (valid && strlen(id) == 0)
@@ -531,9 +516,40 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * n_rows_fixed, guint * n_row
         // If this row was the last row of the invoice...
         if (!valid || (valid && g_strcmp0 (id, running_id->str) != 0))
         {
+            // If invoice should be ignored, remove all rows of this invoice.
+            if (ignore_invoice)
+            {
+                iter = first_row_of_invoice;
+                do
+                {
+                    (*n_rows_ignored)++;
+                    valid = gtk_list_store_remove (store, &iter);
+                    if (valid) gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ID, &id, -1);
+                }
+                while (valid && (g_strcmp0 (id, running_id->str) == 0));
+                
+                if (running_id->len != 0)
+                {
+                    g_string_append_printf (info,
+                                            _("Error(s) in invoice %s, all rows of this invoice ignored.\n"),
+                                            running_id->str);
+                }
+                else
+                {
+                    g_string_append_printf (info,
+                                            _("Error(s) in invoice without id, all rows of this invoice ignored.\n"));
+                }
+                    
+                // Fixes for ignored invoices don't count in the statistics.
+                fixed_for_invoice = 0;
+                
+                ignore_invoice = FALSE;
+            }
+            
             on_first_row_of_invoice = TRUE;
             (*n_rows_fixed) += fixed_for_invoice;
             fixed_for_invoice = 0;
+            invoice_line = 0;
             
             g_free (id);
             g_free (date_opened);
@@ -664,9 +680,9 @@ gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
             g_string_assign(running_id, id);
             first_row_of_invoice = iter;
         
-            if (g_ascii_strcasecmp (type, "BILL") == 0)  // A comment to distract compare
+            if (g_ascii_strcasecmp (type, "BILL") == 0)
                 invoice = gnc_search_bill_on_id (book, id);
-            else if (g_ascii_strcasecmp (type, "INVOICE") == 0) // A comment to distract compare
+            else if (g_ascii_strcasecmp (type, "INVOICE") == 0)
                 invoice = gnc_search_invoice_on_id (book, id);
             DEBUG( "Existing %s ID: %s\n", type, gncInvoiceGetID(invoice));
 

commit 13add9c44d001ed20c3651df842b1a9d9c12a65e
Author: Rob Laan <rob.laan at chello.nl>
Date:   Mon Feb 11 20:32:24 2019 +0100

    Import Bills & Invoices: improve doxygen data, remove obsolete comments

diff --git a/gnucash/import-export/bi-import/dialog-bi-import-gui.c b/gnucash/import-export/bi-import/dialog-bi-import-gui.c
index 05697cb08..125d61f5c 100644
--- a/gnucash/import-export/bi-import/dialog-bi-import-gui.c
+++ b/gnucash/import-export/bi-import/dialog-bi-import-gui.c
@@ -25,6 +25,7 @@
  * @brief GUI handling for bi-import plugin
  * @author Copyright (C) 2009 Sebastian Held <sebastian.held at gmx.de>
  * @author Mike Evans <mikee at saxicola.co.uk>
+ * @author Rob Laan <rob.laan at chello.nl>
  */
 
 #ifdef HAVE_CONFIG_H
diff --git a/gnucash/import-export/bi-import/dialog-bi-import.c b/gnucash/import-export/bi-import/dialog-bi-import.c
index 0ddc0dd98..3c97f4b02 100644
--- a/gnucash/import-export/bi-import/dialog-bi-import.c
+++ b/gnucash/import-export/bi-import/dialog-bi-import.c
@@ -25,9 +25,7 @@
  * @brief core import functions for invoice import plugin
  * @author Copyright (C) 2009 Sebastian Held <sebastian.held at gmx.de>
  * @author Mike Evans <mikee at saxicola.co.uk>
- * @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).
+ * @author Rob Laan <rob.laan at chello.nl>
  */
 
 #ifdef HAVE_CONFIG_H
@@ -75,6 +73,27 @@
 
 static QofLogModule log_module = G_LOG_DOMAIN; //G_LOG_BUSINESS;
 static char * un_escape(char *str);
+
+/** \brief Imports a csv file with invoice data into a GtkListStore.
+ 
+ Opens the csv file and attempts to match each row with the regular
+ expression provided in parser_regexp. This is a regular expression
+ that matches each field of the import row and the user selected field
+ separators (, or ;), optionally with the fields enclosed in quotes.
+ 
+ If the match is succesful, the fields of the import row are transferred to
+ a row in the GtkListStore store. If the the match is not succesful, the
+ row is ignored. Maintains information about number of rows imported,
+ the number of rows ignored, and the actual ignored rows.
+ 
+ @param filename      The csv filename to read
+ @param parser_regexp The regular expression with which to match the import rows
+ @param store         To store the matched data
+ @param max_rows      The maximum number of rows to import; use 0 for no maximum.
+ @param stats         Return information about matched and non-matched rows. Use NULL if the information is not required.
+ 
+ */
+
 bi_import_result
 gnc_bi_import_read_file (const gchar * filename, const gchar * parser_regexp,
                          GtkListStore * store, guint max_rows,
@@ -217,12 +236,36 @@ gnc_bi_import_read_file (const gchar * filename, const gchar * parser_regexp,
 }
 
 
-//! \brief try to fix some common errors in the csv representation of invoices
-//! * corrects the date format
-//! * corrects ambiguous values in multi line invoices
-//! * ensures customer exists
-//! * if quantity is unset, set to 1
-//! * if price is unset, delete row.
+/** \brief Adjusts and validates invoice import data.
+ 
+ Replaces missing or invalid data with defaults:
+ - if quantity is not set, default to 1
+ - if date_opened is not set or invalid, default to today
+ - if date is not set or invalid, default to date_opened
+ - if due date is not set or invalid, default to date_posted
+ 
+ Validates the import data; any error causes all rows of the same invoice
+ to be deleted from the import data:
+ - id is not set
+ - owner_id is not set, or customer/vendor does not exist
+ - date_posted is not valid
+ - account_posted does not exist
+ - account posted is not the applicable type, A/P or A/R
+ - price is not set
+ - account does not exist
+ 
+ Adjustment and validation for header fields is only done for the first row of an invoice,
+ which is assumed to hold the header data for all items of the same invoice.
+ Currency related validation is done in subsqequent processing by gnc_bi_import_create_bis.
+ 
+ @param store Holds the rows of invoice import data
+ @param n_rows_fixed Increased for every data row that is adjusted in this function
+ @param n_rows_ignored Increased for every data row that is deleted in this function
+ @param info Updated with the error messages from this function
+ @param type The type of the import data, BILL or INVOICE
+ 
+ */
+
 void
 gnc_bi_import_fix_bis (GtkListStore * store, guint * n_rows_fixed, guint * n_rows_ignored,
                        GString * info, gchar *type)
@@ -237,9 +280,6 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * n_rows_fixed, guint * n_row
     gint row = 1, fixed_for_invoice = 0;
     const gchar* date_format_string = qof_date_format_get_string (qof_date_format_get()); // Get the user set date format string
 
-
-    //date_format_string = qof_date_format_get_string (qof_date_format_get());
-
     DEBUG("date_format_string: %s",date_format_string);
     // allow the call to this function with only GtkListeStore* specified
     if (!n_rows_fixed)
@@ -518,14 +558,27 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * n_rows_fixed, guint * n_row
 }
 
 
-/***********************************************************************
- * @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
- ***********************************************************************/
+/** \brief Creates and updates invoices from validated import data.
+ 
+ Loops through the import data to create and update invoices.
+ The first data row for an invoice is assumed to hold the header data.
+ 
+ If an invoice already exists, the user is asked, once per import,
+ to confirm that invoices should be updated.
+ If not confirmed, any rows for existing invoices are ignored.
+ If confirmed, entries are added to existing invoices.
+ Posted invoices, however, are never updated.
+ 
+ If the field date_posted is set, the system will
+ attempt to also post the invoice. The system will not
+ post the invoice if the entries of the invoice hold different currencies,
+ or if the currency of the invoice differs from the currency of the account_posted.
+ 
+ As per user selection, the system displays tabs for either all affected invoices,
+ all affected invoices not yet posted, or no invoices at all.
+ 
+ */
+
 void
 gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
                           guint * n_invoices_created,
@@ -605,14 +658,6 @@ gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
                             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 reasonable 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 (on_first_row_of_invoice)
         {
@@ -730,7 +775,6 @@ gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
         gnc_exp_parser_parse (quantity, &value, NULL);
         // Need to set the denom appropriately else we get stupid rounding errors.
         value = gnc_numeric_convert (value, denom * 100, GNC_HOW_RND_NEVER);
-        //DEBUG("qty = %s",gnc_num_dbg_to_string(value));
         gncEntrySetQuantity (entry, value);
         acc = gnc_account_lookup_for_register (gnc_get_current_root_account (),
                                                account);
@@ -754,7 +798,6 @@ gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
             value = gnc_numeric_zero();
             gnc_exp_parser_parse (price, &value, NULL);
             value = gnc_numeric_convert (value, denom * 100, GNC_HOW_RND_NEVER);
-            //DEBUG("price = %s",gnc_num_dbg_to_string(value));
             gncEntrySetInvPrice (entry, value);
             gncEntrySetInvTaxable (entry, text2bool (taxable));
             gncEntrySetInvTaxIncluded (entry, text2bool (taxincluded));
diff --git a/gnucash/import-export/bi-import/dialog-bi-import.h b/gnucash/import-export/bi-import/dialog-bi-import.h
index 2407edc8d..f1f53a0a8 100644
--- a/gnucash/import-export/bi-import/dialog-bi-import.h
+++ b/gnucash/import-export/bi-import/dialog-bi-import.h
@@ -26,6 +26,7 @@
  * @brief core import functions for invoice import plugin
  * @author Copyright (C) 2009 Sebastian Held <sebastian.held at gmx.de>
  * @author Mike Evans <mikee at saxicola.co.uk>
+ * @author Rob Laan <rob.laan at chello.nl>
  */
 
 #ifndef GNC_PLUGIN_BI_IMPORT_H

commit 0423fd72ea8e11eb990ccd1549f0cb43e67cfe23
Author: Rob Laan <rob.laan at chello.nl>
Date:   Mon Feb 11 10:28:56 2019 +0100

    Import Bills & Invoices: Improve feedback to user

diff --git a/gnucash/import-export/bi-import/dialog-bi-import-gui.c b/gnucash/import-export/bi-import/dialog-bi-import-gui.c
index 66e362f58..05697cb08 100644
--- a/gnucash/import-export/bi-import/dialog-bi-import-gui.c
+++ b/gnucash/import-export/bi-import/dialog-bi-import-gui.c
@@ -216,12 +216,13 @@ gnc_bi_import_gui_ok_cb (GtkWidget *widget, gpointer data)
     if (res == RESULT_OK)
     {
         gnc_bi_import_fix_bis (gui->store, &n_fixed, &n_deleted, info, gui->type);
-        gnc_bi_import_create_bis (gui->store, gui->book, &n_invoices_created, &n_invoices_updated,
+        gnc_bi_import_create_bis (gui->store, gui->book, &n_invoices_created, &n_invoices_updated, &n_deleted,
                                   gui->type, gui->open_mode, info, gui->parent);
         if (info->len > 0)
             gnc_info_dialog (GTK_WINDOW (gui->dialog), "%s", info->str);
         g_string_free( info, TRUE );
-        gnc_info_dialog (GTK_WINDOW (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);
+        gnc_info_dialog (GTK_WINDOW (gui->dialog), _("Import:\n- rows ignored: %i\n- rows imported: %i\n\nValidation & processing:\n- rows fixed: %u\n- rows ignored: %u\n- invoices created: %u\n- invoices updated: %u"),
+                         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);
 
diff --git a/gnucash/import-export/bi-import/dialog-bi-import.c b/gnucash/import-export/bi-import/dialog-bi-import.c
index 91b1cef33..0ddc0dd98 100644
--- a/gnucash/import-export/bi-import/dialog-bi-import.c
+++ b/gnucash/import-export/bi-import/dialog-bi-import.c
@@ -224,7 +224,7 @@ gnc_bi_import_read_file (const gchar * filename, const gchar * parser_regexp,
 //! * 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,
+gnc_bi_import_fix_bis (GtkListStore * store, guint * n_rows_fixed, guint * n_rows_ignored,
                        GString * info, gchar *type)
 {
     GtkTreeIter iter, first_row_of_invoice;
@@ -242,18 +242,20 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
 
     DEBUG("date_format_string: %s",date_format_string);
     // allow the call to this function with only GtkListeStore* specified
-    if (!fixed)
-        fixed = &dummy;
-    if (!deleted)
-        deleted = &dummy;
+    if (!n_rows_fixed)
+        n_rows_fixed = &dummy;
+    if (!n_rows_ignored)
+        n_rows_ignored = &dummy;
 
-    *fixed = 0;
-    *deleted = 0;
+    *n_rows_fixed = 0;
+    *n_rows_ignored = 0;
     
     // Init control variables
     running_id = g_string_new("");
     ignore_invoice = FALSE;
     on_first_row_of_invoice = TRUE;
+    
+    g_string_append_printf (info, _("Validation...\n") );
 
     // Walk through the list, reading each row.
     valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter);
@@ -463,7 +465,7 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
             iter = first_row_of_invoice;
             while (valid && g_strcmp0 (id, running_id->str) == 0)
             {
-                (*deleted)++;
+                (*n_rows_ignored)++;
                 valid = gtk_list_store_remove (store, &iter);
                 if (valid) gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ID, &id, -1);
             }
@@ -490,8 +492,9 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
         if (!valid || (valid && g_strcmp0 (id, running_id->str) != 0))
         {
             on_first_row_of_invoice = TRUE;
-            (*fixed) += fixed_for_invoice;
+            (*n_rows_fixed) += fixed_for_invoice;
             fixed_for_invoice = 0;
+            
             g_free (id);
             g_free (date_opened);
             g_free (date_posted);
@@ -500,7 +503,7 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
             g_free (owner_id);
         }
         else on_first_row_of_invoice = FALSE;
-
+        
         g_free (date);
         g_free (account);
         g_free (quantity);
@@ -527,6 +530,7 @@ void
 gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
                           guint * n_invoices_created,
                           guint * n_invoices_updated,
+                          guint * n_rows_ignored,
                           gchar * type, gchar * open_mode, GString * info,
                           GtkWindow *parent)
 {
@@ -572,6 +576,8 @@ gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
     on_first_row_of_invoice = TRUE;
     running_id = g_string_new("");
     
+    g_string_append_printf (info, _("\nProcessing...\n") );
+    
     valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter);
     while (valid)
     {
@@ -673,6 +679,7 @@ gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
                     g_string_append_printf (info,_("Invoice %s not updated because it already exists.\n"),id);
                     while (valid && g_strcmp0 (id, running_id->str) == 0)
                     {
+                        (*n_rows_ignored)++;
                         valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
                         if (valid)
                             gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ID, &id, -1);
@@ -687,6 +694,7 @@ gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
                     g_string_append_printf (info,_("Invoice %s not updated because it is already posted.\n"),id);
                     while (valid && g_strcmp0 (id, running_id->str) == 0)
                     {
+                        (*n_rows_ignored)++;
                         valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
                         if (valid)
                             gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ID, &id, -1);
@@ -849,11 +857,15 @@ gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
                 iw =  gnc_ui_invoice_edit (parent, invoice);
                 gnc_plugin_page_invoice_new (iw);
             }
-            
+        
             // The next row will be for a new invoice.
             on_first_row_of_invoice = TRUE;
         }
     }
+    
+    if (*n_invoices_updated + *n_invoices_created == 0)
+        g_string_append_printf (info, _("Nothing to process.\n"));
+    
     // cleanup
     g_free (id);
     g_free (date_opened);
diff --git a/gnucash/import-export/bi-import/dialog-bi-import.h b/gnucash/import-export/bi-import/dialog-bi-import.h
index 649b29067..2407edc8d 100644
--- a/gnucash/import-export/bi-import/dialog-bi-import.h
+++ b/gnucash/import-export/bi-import/dialog-bi-import.h
@@ -69,7 +69,7 @@ gnc_bi_import_fix_bis (GtkListStore *store, guint *fixed, guint *deleted, GStrin
 
 void
 gnc_bi_import_create_bis (GtkListStore *store, QofBook *book, guint *n_invoices_created,
-                          guint *n_invoices_updated,	gchar *type, gchar *open_mode, GString * info,
+                          guint *n_invoices_updated, guint *n_rows_ignored,	gchar *type, gchar *open_mode, GString * info,
                           GtkWindow *parent);
 
 

commit 8c31b1c99ac62e05b20670ecc0d55ef8519167cc
Author: Rob Laan <rob.laan at chello.nl>
Date:   Sun Feb 10 20:22:52 2019 +0100

    Fix bug 797023 Import Bills & Invoices: no user confirmation requested for update of invoices, if new invoice is created first

diff --git a/gnucash/import-export/bi-import/dialog-bi-import.c b/gnucash/import-export/bi-import/dialog-bi-import.c
index e082f9dc1..91b1cef33 100644
--- a/gnucash/import-export/bi-import/dialog-bi-import.c
+++ b/gnucash/import-export/bi-import/dialog-bi-import.c
@@ -230,7 +230,7 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
     GtkTreeIter iter, first_row_of_invoice;
     gboolean valid, row_fixed, on_first_row_of_invoice, ignore_invoice;
     gchar *id = NULL, *date_opened = NULL, *date_posted = NULL, *due_date = NULL, *account_posted = NULL,
-    *owner_id = NULL, *date = NULL, *account = NULL, *quantity = NULL, *price = NULL;
+        *owner_id = NULL, *date = NULL, *account = NULL, *quantity = NULL, *price = NULL;
     GString *running_id;
     Account *acc = NULL;
     guint dummy;
@@ -492,7 +492,6 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
             on_first_row_of_invoice = TRUE;
             (*fixed) += fixed_for_invoice;
             fixed_for_invoice = 0;
-            
             g_free (id);
             g_free (date_opened);
             g_free (date_posted);
@@ -546,7 +545,7 @@ gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
     gnc_numeric value;
     GncOwner *owner;
     Account *acc = NULL;
-    enum update {YES = GTK_RESPONSE_YES, NO = GTK_RESPONSE_NO} update;
+    enum update {YES = GTK_RESPONSE_YES, NO = GTK_RESPONSE_NO, NOT_ASKED = GTK_RESPONSE_NONE} update;
     GtkWidget *dialog;
     time64 today;
     InvoiceWindow *iw;
@@ -569,7 +568,7 @@ gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
     *n_invoices_updated = 0;
 
     invoice = NULL;
-    update = NO;
+    update = NOT_ASKED;
     on_first_row_of_invoice = TRUE;
     running_id = g_string_new("");
     
@@ -613,100 +612,94 @@ gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
         {
             g_string_assign(running_id, id);
             first_row_of_invoice = iter;
-        }
         
-        if (g_ascii_strcasecmp (type, "BILL") == 0)
-            invoice = gnc_search_bill_on_id (book, id);
-        else if (g_ascii_strcasecmp (type, "INVOICE") == 0)
-            invoice = gnc_search_invoice_on_id (book, id);
-        DEBUG( "Existing %s ID: %s\n", type, gncInvoiceGetID(invoice));
-
-        // If the search is empty then there is no existing invoice so make a new one
-        if (invoice == NULL)
-        {
-             DEBUG( "Creating a new : %s\n", type );
-            // new invoice
-            invoice = gncInvoiceCreate (book);
-            /* Protect against thrashing the DB and trying to write the invoice
-             * record prematurely */
-            gncInvoiceBeginEdit (invoice);
-            gncInvoiceSetID (invoice, id);
-            owner = gncOwnerNew ();
-            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
-            qof_scan_date (date_opened, &day, &month, &year);
-            gncInvoiceSetDateOpened (invoice,
-                                     gnc_dmy2time64 (day, month, year));
-            gncInvoiceSetBillingID (invoice, billing_id ? billing_id : "");
-            notes = un_escape(notes);
-            gncInvoiceSetNotes (invoice, notes ? notes : "");
-            gncInvoiceSetActive (invoice, TRUE);
-            //if (g_ascii_strcasecmp(type,"INVOICE"))gncInvoiceSetBillTo( invoice, billto );
-            (*n_invoices_created)++;
-            update = YES;
-
-            gncInvoiceCommitEdit (invoice);
-        }
-// 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?
+            if (g_ascii_strcasecmp (type, "BILL") == 0)  // A comment to distract compare
+                invoice = gnc_search_bill_on_id (book, id);
+            else if (g_ascii_strcasecmp (type, "INVOICE") == 0) // A comment to distract compare
+                invoice = gnc_search_invoice_on_id (book, id);
+            DEBUG( "Existing %s ID: %s\n", type, gncInvoiceGetID(invoice));
+
+            // If the search is empty then there is no existing invoice so make a new one
+            if (invoice == NULL)
             {
-                valid =
-                    gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
-                continue;		// If already posted then never import
+                 DEBUG( "Creating a new : %s\n", type );
+                // new invoice
+                invoice = gncInvoiceCreate (book);
+                /* Protect against thrashing the DB and trying to write the invoice
+                 * record prematurely */
+                gncInvoiceBeginEdit (invoice);
+                gncInvoiceSetID (invoice, id);
+                owner = gncOwnerNew ();
+                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
+                qof_scan_date (date_opened, &day, &month, &year);
+                gncInvoiceSetDateOpened (invoice,
+                                         gnc_dmy2time64 (day, month, year));
+                gncInvoiceSetBillingID (invoice, billing_id ? billing_id : "");
+                notes = un_escape(notes);
+                gncInvoiceSetNotes (invoice, notes ? notes : "");
+                gncInvoiceSetActive (invoice, TRUE);
+                //if (g_ascii_strcasecmp(type,"INVOICE"))gncInvoiceSetBillTo( invoice, billto );
+                (*n_invoices_created)++;
+                g_string_append_printf (info, _("Invoice %s created.\n"),id);
+                
+                gncInvoiceCommitEdit (invoice);
             }
-            if (update != YES)	// Pop up a dialog to ask if updates are the expected action
+            else			// Dealing with an existing invoice.
             {
-                dialog = gtk_message_dialog_new (parent,
-                                                 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);
+                // For the first existing invoice in the import file,
+                // ask the user to confirm update of existing invoices.
+                if (update == NOT_ASKED)
+                {
+                    dialog = gtk_message_dialog_new (parent,
+                                                     GTK_DIALOG_MODAL,
+                                                     GTK_MESSAGE_ERROR,
+                                                     GTK_BUTTONS_YES_NO,
+                                                     "%s",
+                                                     _("Do you want to update existing bills/invoices?"));
+                    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 (billing_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;
+                    // If the user does not want to update existing invoices, ignore all rows of the invoice.
+                    g_string_append_printf (info,_("Invoice %s not updated because it already exists.\n"),id);
+                    while (valid && g_strcmp0 (id, running_id->str) == 0)
+                    {
+                        valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
+                        if (valid)
+                            gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ID, &id, -1);
+                    }
+                    on_first_row_of_invoice = TRUE;
+                    continue;
                 }
+                
+                if (gncInvoiceIsPosted (invoice))
+                {
+                    // If the invoice is already posted, ignore all rows of the invoice.
+                    g_string_append_printf (info,_("Invoice %s not updated because it is already posted.\n"),id);
+                    while (valid && g_strcmp0 (id, running_id->str) == 0)
+                    {
+                        valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
+                        if (valid)
+                            gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ID, &id, -1);
+                    }
+                    on_first_row_of_invoice = TRUE;
+                    continue;
+                }
+
+                (*n_invoices_updated)++;
+                g_string_append_printf (info, _("Invoice %s updated.\n"),id);
             }
-            (*n_invoices_updated)++;
         }
 
-
         // Add entry to invoice/bill
         entry = gncEntryCreate (book);
         gncEntryBeginEdit(entry);

commit 66950e630b3df07652c3e24bfed9c0d5ee9f6e55
Author: Rob Laan <rob.laan at chello.nl>
Date:   Sun Feb 10 17:30:11 2019 +0100

    Fix bug 796984 Import Bills & Invoices: date of line item is not validated; can lead to unexpected results

diff --git a/gnucash/import-export/bi-import/dialog-bi-import.c b/gnucash/import-export/bi-import/dialog-bi-import.c
index b1706f33f..e082f9dc1 100644
--- a/gnucash/import-export/bi-import/dialog-bi-import.c
+++ b/gnucash/import-export/bi-import/dialog-bi-import.c
@@ -230,7 +230,7 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
     GtkTreeIter iter, first_row_of_invoice;
     gboolean valid, row_fixed, on_first_row_of_invoice, ignore_invoice;
     gchar *id = NULL, *date_opened = NULL, *date_posted = NULL, *due_date = NULL, *account_posted = NULL,
-        *owner_id = NULL, *date = NULL, *account = NULL, *quantity = NULL, *price = NULL;
+    *owner_id = NULL, *date = NULL, *account = NULL, *quantity = NULL, *price = NULL;
     GString *running_id;
     Account *acc = NULL;
     guint dummy;
@@ -255,26 +255,23 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
     ignore_invoice = FALSE;
     on_first_row_of_invoice = TRUE;
 
+    // Walk through the list, reading each row.
     valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter);
     while (valid)
     {
         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,
-                            DUE_DATE, &due_date,
-                            ACCOUNT_POSTED, &account_posted,
-                            OWNER_ID, &owner_id,
-                            DATE, &date,
-                            ACCOUNT, &account,
-                            QUANTITY, &quantity, PRICE, &price, -1);
-        
         //  If this is a row for a new invoice id, validate header values.
         if (on_first_row_of_invoice)
         {
+            gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
+                                ID, &id,
+                                DATE_OPENED, &date_opened,
+                                DATE_POSTED, &date_posted,
+                                DUE_DATE, &due_date,
+                                ACCOUNT_POSTED, &account_posted,
+                                OWNER_ID, &owner_id, -1);
+            
             g_string_assign (running_id, id);
             first_row_of_invoice = iter;
             
@@ -400,6 +397,16 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
             }
         }
         
+        // Validate and fix item date for each row.
+        
+        // Get item data.
+        gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
+                            DATE, &date,
+                            ACCOUNT, &account,
+                            QUANTITY, &quantity,
+                            PRICE, &price, -1);
+
+        
         // Validate the price.
         if (strlen (price) == 0)
         {
@@ -435,6 +442,16 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
                 gtk_list_store_set (store, &iter, QUANTITY, "1", -1);
                 row_fixed = TRUE;
             }
+            
+            // Verify the item date
+            if(!isDateValid(date))
+            {
+                // Invalid item date, replace with date opened
+                gtk_list_store_set (store, &iter, DATE,
+                                    date_opened, -1);
+                row_fixed = TRUE;
+            }
+
         }
         if (row_fixed) ++fixed_for_invoice;
         
@@ -475,15 +492,16 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
             on_first_row_of_invoice = TRUE;
             (*fixed) += fixed_for_invoice;
             fixed_for_invoice = 0;
+            
+            g_free (id);
+            g_free (date_opened);
+            g_free (date_posted);
+            g_free (due_date);
+            g_free (account_posted);
+            g_free (owner_id);
         }
         else on_first_row_of_invoice = FALSE;
 
-        g_free (id);
-        g_free (date_opened);
-        g_free (date_posted);
-        g_free (due_date);
-        g_free (account_posted);
-        g_free (owner_id);
         g_free (date);
         g_free (account);
         g_free (quantity);
@@ -689,31 +707,19 @@ gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
         }
 
 
-        // add entry to invoice/bill
+        // Add entry to invoice/bill
         entry = gncEntryCreate (book);
         gncEntryBeginEdit(entry);
         currency = gncInvoiceGetCurrency(invoice);
         if (currency) denom = gnc_commodity_get_fraction(currency);
-        // FIXME: Must check for the return value of qof_scan_date!
         qof_scan_date (date, &day, &month, &year);
         {
             GDate *date = g_date_new_dmy(day, month, year);
             gncEntrySetDateGDate (entry, date);
             g_date_free (date);
         }
-        today = gnc_time (NULL);	// set today to the current date
-        if (strlen (date) != 0)	// If a date is specified in CSV
-        {
-            GDate *date = g_date_new_dmy(day, month, year);
-            gncEntrySetDateGDate(entry, date);
-            gncEntrySetDateEntered(entry, gnc_dmy2time64 (day, month, year));
-        }
-        else
-        {
-            GDate *date = gnc_g_date_new_today();
-            gncEntrySetDateGDate(entry, date);
-            gncEntrySetDateEntered(entry, today);
-        }
+        today = gnc_time (NULL);
+        gncEntrySetDateEntered(entry, today);
         // Remove escaped quotes
         desc = un_escape(desc);
         notes = un_escape(notes);

commit 53aec754e3d76140647ae7665f992019bf66f91a
Author: Rob Laan <rob.laan at chello.nl>
Date:   Sun Feb 10 17:24:35 2019 +0100

    Fix bug 79698 Import Bills & Invoices: option 'open not yet posted docs in tab' does not open tab if invoice could not be posted

diff --git a/gnucash/import-export/bi-import/dialog-bi-import.c b/gnucash/import-export/bi-import/dialog-bi-import.c
index 7c0af7793..b1706f33f 100644
--- a/gnucash/import-export/bi-import/dialog-bi-import.c
+++ b/gnucash/import-export/bi-import/dialog-bi-import.c
@@ -513,7 +513,7 @@ gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
                           gchar * type, gchar * open_mode, GString * info,
                           GtkWindow *parent)
 {
-    gboolean valid, on_first_row_of_invoice;
+    gboolean valid, on_first_row_of_invoice, invoice_posted;
     GtkTreeIter iter, first_row_of_invoice;
     gchar *id = NULL, *date_opened = NULL, *owner_id = NULL, *billing_id = NULL, *notes = NULL;
     gchar *date = NULL, *desc = NULL, *action = NULL, *account = NULL, *quantity = NULL,
@@ -783,6 +783,7 @@ gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
                                 ACCOUNT_POSTED, &account_posted,
                                 MEMO_POSTED, &memo_posted,
                                 ACCU_SPLITS, &accumulatesplits, -1);
+            invoice_posted = FALSE;
 
             if (strlen(date_posted) != 0)
             {
@@ -820,7 +821,8 @@ gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
                                              text2bool (accumulatesplits),
                                              auto_pay);
                         PWARN("Invoice %s posted",id);
-                         g_string_append_printf (info, _("Invoice %s posted.\n"),id);
+                        invoice_posted = TRUE;
+                        g_string_append_printf (info, _("Invoice %s posted.\n"),id);
                     }
                     else // No match! Don't post it.
                     {
@@ -843,7 +845,7 @@ gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
             // open new bill / invoice in a tab, if requested
             if (g_ascii_strcasecmp(open_mode, "ALL") == 0
                     || (g_ascii_strcasecmp(open_mode, "NOT_POSTED") == 0
-                        && strlen(date_posted) == 0))
+                        && !invoice_posted))
             {
                 iw =  gnc_ui_invoice_edit (parent, invoice);
                 gnc_plugin_page_invoice_new (iw);

commit 75d45653095b18b4ba4ab88cf0486779ecc248c1
Author: Rob Laan <rob.laan at chello.nl>
Date:   Sun Feb 10 17:14:38 2019 +0100

    Fix bug 797025 Import Bills & Invoices: the type of the post to account is not validated - enabling A/P and A/R postings on regular accounts

diff --git a/gnucash/import-export/bi-import/dialog-bi-import.c b/gnucash/import-export/bi-import/dialog-bi-import.c
index bb791a92c..7c0af7793 100644
--- a/gnucash/import-export/bi-import/dialog-bi-import.c
+++ b/gnucash/import-export/bi-import/dialog-bi-import.c
@@ -229,7 +229,7 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
 {
     GtkTreeIter iter, first_row_of_invoice;
     gboolean valid, row_fixed, on_first_row_of_invoice, ignore_invoice;
-    gchar *id = NULL, *date_opened = NULL, *date_posted = NULL, *due_date = NULL,
+    gchar *id = NULL, *date_opened = NULL, *date_posted = NULL, *due_date = NULL, *account_posted = NULL,
         *owner_id = NULL, *date = NULL, *account = NULL, *quantity = NULL, *price = NULL;
     GString *running_id;
     Account *acc = NULL;
@@ -266,6 +266,7 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
                             DATE_OPENED, &date_opened,
                             DATE_POSTED, &date_posted,
                             DUE_DATE, &due_date,
+                            ACCOUNT_POSTED, &account_posted,
                             OWNER_ID, &owner_id,
                             DATE, &date,
                             ACCOUNT, &account,
@@ -335,6 +336,45 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
                                             row, id, date_posted);
                 }
                 
+                // Validate account posted.
+                // Account should exists, and should be of type A/R for invoices, A/P for bills.
+                if (!ignore_invoice)
+                {
+                    acc = gnc_account_lookup_for_register
+                    (gnc_get_current_root_account (), account_posted);
+                    if (acc == NULL)
+                    {
+                        ignore_invoice = TRUE;
+                        g_string_append_printf (info,
+                                                _("Row %d: invoice %s ignored, account %s does not exist.\n"),
+                                                row, id,account_posted);
+                    }
+                    else
+                    {
+                        if (g_ascii_strcasecmp (type, "BILL") == 0)
+                        {
+                            
+                            if (xaccAccountGetType (acc) != ACCT_TYPE_PAYABLE)
+                            {
+                                ignore_invoice = TRUE;
+                                g_string_append_printf (info,
+                                                        _("Row %d: invoice %s ignored, account %s is not of type Accounts Payable.\n"),
+                                                        row, id, account_posted);
+                            }
+                        }
+                        else if (g_ascii_strcasecmp (type, "INVOICE") == 0)
+                        {
+                            if (xaccAccountGetType (acc) != ACCT_TYPE_RECEIVABLE)
+                            {
+                                ignore_invoice = TRUE;
+                                g_string_append_printf (info,
+                                                        _("Row %d: invoice %s ignored, account %s is not of type Accounts Receivable.\n"),
+                                                        row, id, account_posted);
+                            }
+                        }
+                    }
+                }
+                
                 // Verify the due date.
                 if (!ignore_invoice && !isDateValid(due_date))
                 {
@@ -442,6 +482,7 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
         g_free (date_opened);
         g_free (date_posted);
         g_free (due_date);
+        g_free (account_posted);
         g_free (owner_id);
         g_free (date);
         g_free (account);
@@ -767,32 +808,24 @@ gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
                 {
                     acc = gnc_account_lookup_for_register
                           (gnc_get_current_root_account (), account_posted);
-                    if(acc != NULL) // Is the account real?
-                    {                        
-                        // Check if the currencies match
-                        if(gncInvoiceGetCurrency(invoice) == gnc_account_get_currency_or_parent(acc))
-                        {
-                            qof_scan_date (date_posted, &day, &month, &year);
-                            p_date = gnc_dmy2time64 (day, month, year);
-                            qof_scan_date (due_date, &day, &month, &year);
-                            d_date = gnc_dmy2time64 (day, month, year);
-                            gncInvoicePostToAccount (invoice, acc, p_date, d_date,
-                                                 memo_posted,
-                                                 text2bool (accumulatesplits),
-                                                 auto_pay);
-                            PWARN("Invoice %s posted",id);
-                             g_string_append_printf (info, _("Invoice %s posted.\n"),id);
-                        }
-                        else // No match! Don't post it.
-                        {
-                            PWARN("Invoice %s NOT posted because currencies don't match", id);
-                            g_string_append_printf (info,_("Invoice %s NOT posted because currencies don't match.\n"), id);
-                        }
+                    // Check if the currencies match
+                    if(gncInvoiceGetCurrency(invoice) == gnc_account_get_currency_or_parent(acc))
+                    {
+                        qof_scan_date (date_posted, &day, &month, &year);
+                        p_date = gnc_dmy2time64 (day, month, year);
+                        qof_scan_date (due_date, &day, &month, &year);
+                        d_date = gnc_dmy2time64 (day, month, year);
+                        gncInvoicePostToAccount (invoice, acc, p_date, d_date,
+                                             memo_posted,
+                                             text2bool (accumulatesplits),
+                                             auto_pay);
+                        PWARN("Invoice %s posted",id);
+                         g_string_append_printf (info, _("Invoice %s posted.\n"),id);
                     }
-                    else
+                    else // No match! Don't post it.
                     {
-                        PWARN("Cannot post invoice %s because account name \"%s\" is invalid!",id,account_posted);
-                        g_string_append_printf (info,_("Cannot post invoice %s because account name \"%s\" is invalid!\n"),id,account_posted);
+                        PWARN("Invoice %s NOT posted because currencies don't match", id);
+                        g_string_append_printf (info,_("Invoice %s NOT posted because currencies don't match.\n"), id);
                     }
                 }
                 else

commit 94a4d7620e82f1d98a5f7f479bfd14d38f9bd0ab
Author: Rob Laan <rob.laan at chello.nl>
Date:   Sun Feb 10 17:05:13 2019 +0100

    Fix bug 79702 Import Bills & Invoices: expense/income account is not validated - leads to one-sided posting if account is invalid

diff --git a/gnucash/import-export/bi-import/dialog-bi-import.c b/gnucash/import-export/bi-import/dialog-bi-import.c
index 7dbfae929..bb791a92c 100644
--- a/gnucash/import-export/bi-import/dialog-bi-import.c
+++ b/gnucash/import-export/bi-import/dialog-bi-import.c
@@ -230,8 +230,9 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
     GtkTreeIter iter, first_row_of_invoice;
     gboolean valid, row_fixed, on_first_row_of_invoice, ignore_invoice;
     gchar *id = NULL, *date_opened = NULL, *date_posted = NULL, *due_date = NULL,
-        *owner_id = NULL, *date = NULL, *quantity = NULL, *price = NULL;
+        *owner_id = NULL, *date = NULL, *account = NULL, *quantity = NULL, *price = NULL;
     GString *running_id;
+    Account *acc = NULL;
     guint dummy;
     gint row = 1, fixed_for_invoice = 0;
     const gchar* date_format_string = qof_date_format_get_string (qof_date_format_get()); // Get the user set date format string
@@ -267,6 +268,7 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
                             DUE_DATE, &due_date,
                             OWNER_ID, &owner_id,
                             DATE, &date,
+                            ACCOUNT, &account,
                             QUANTITY, &quantity, PRICE, &price, -1);
         
         //  If this is a row for a new invoice id, validate header values.
@@ -368,6 +370,20 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
                                     row, id);
         }
 
+        // Validate the account
+        if (!ignore_invoice)
+        {
+            acc = gnc_account_lookup_for_register (gnc_get_current_root_account (),
+                                                   account);
+            if (acc == NULL)
+            {
+                ignore_invoice = TRUE;
+                g_string_append_printf (info,
+                                        _("Row %d: invoice %s ignored, account %s does not exist.\n"),
+                                        row, id,account);
+            }
+        }
+        
         // Fix item data.
         if (!ignore_invoice)
         {
@@ -428,6 +444,7 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
         g_free (due_date);
         g_free (owner_id);
         g_free (date);
+        g_free (account);
         g_free (quantity);
         g_free (price);
 

commit 91f68e94641ed8545184d5ef7ac7128dc9c07818
Author: Rob Laan <rob.laan at chello.nl>
Date:   Sun Feb 10 16:55:10 2019 +0100

    Fix bug 796986 Import Bills & Invoices: in case of invalid posting date in import file, invoice is posted with unexpected date

diff --git a/gnucash/import-export/bi-import/dialog-bi-import.c b/gnucash/import-export/bi-import/dialog-bi-import.c
index a65dfe585..7dbfae929 100644
--- a/gnucash/import-export/bi-import/dialog-bi-import.c
+++ b/gnucash/import-export/bi-import/dialog-bi-import.c
@@ -321,6 +321,28 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
                 }
             }
             
+            if (!ignore_invoice && strlen(date_posted) != 0)
+            {
+                // Validate the date posted.
+                if (!isDateValid(date_posted))
+                {
+                    // Invalid date posted in first row of invoice, ignore the invoice
+                    ignore_invoice = TRUE;
+                    g_string_append_printf (info,
+                                            _("Row %d: invoice %s ignored, %s is not a valid posting date.\n"),
+                                            row, id, date_posted);
+                }
+                
+                // Verify the due date.
+                if (!ignore_invoice && !isDateValid(due_date))
+                {
+                    // Fix this by using the date posted.
+                    gtk_list_store_set (store, &iter, DUE_DATE,
+                                        date_posted, -1);
+                    row_fixed = TRUE;
+                }
+            }
+            
             // Verify the date opened.
             if(!ignore_invoice && !isDateValid(date_opened))
             {
@@ -334,7 +356,6 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
                                     temp, -1);
                 row_fixed = TRUE;
             }
-
         }
         
         // Validate the price.
@@ -404,6 +425,7 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
         g_free (id);
         g_free (date_opened);
         g_free (date_posted);
+        g_free (due_date);
         g_free (owner_id);
         g_free (date);
         g_free (quantity);
@@ -726,14 +748,6 @@ gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
                 // Only auto-post if there's a single currency involved
                 if(curr_count == 0)
                 {
-                    p_date = gnc_dmy2time64 (day, month, year);
-                    // Check for the return value of qof_scan_date
-                    if(qof_scan_date (due_date, &day, &month, &year)) // obtains the due date, or leaves it at date_posted
-                    {	
-                        d_date = gnc_dmy2time64 (day, month, year);
-                    }
-                    else
-                        d_date = p_date;
                     acc = gnc_account_lookup_for_register
                           (gnc_get_current_root_account (), account_posted);
                     if(acc != NULL) // Is the account real?
@@ -741,6 +755,10 @@ gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
                         // Check if the currencies match
                         if(gncInvoiceGetCurrency(invoice) == gnc_account_get_currency_or_parent(acc))
                         {
+                            qof_scan_date (date_posted, &day, &month, &year);
+                            p_date = gnc_dmy2time64 (day, month, year);
+                            qof_scan_date (due_date, &day, &month, &year);
+                            d_date = gnc_dmy2time64 (day, month, year);
                             gncInvoicePostToAccount (invoice, acc, p_date, d_date,
                                                  memo_posted,
                                                  text2bool (accumulatesplits),

commit ffd92534cc7353113bc3fe969a4cb6c31d154867
Author: Rob Laan <rob.laan at chello.nl>
Date:   Sun Feb 10 16:39:12 2019 +0100

    Import Bills & Invoices: in case of error on import row, ignore full invoice instead of just that row
    
    Prevents invoices and bills with incorrect total values.
    Considerably simplifies follow up for the user, after an import with error data rows.

diff --git a/gnucash/import-export/bi-import/dialog-bi-import.c b/gnucash/import-export/bi-import/dialog-bi-import.c
index 8732160bd..a65dfe585 100644
--- a/gnucash/import-export/bi-import/dialog-bi-import.c
+++ b/gnucash/import-export/bi-import/dialog-bi-import.c
@@ -222,18 +222,18 @@ gnc_bi_import_read_file (const gchar * filename, const gchar * parser_regexp,
 //! * corrects ambiguous values in multi line invoices
 //! * ensures customer exists
 //! * if quantity is unset, set to 1
-//! * if price is unset, delete row
+//! * if price is unset, delete row.
 void
 gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
                        GString * info, gchar *type)
 {
-    GtkTreeIter iter;
-    gboolean valid, row_deleted, row_fixed, on_first_row_of_invoice;
+    GtkTreeIter iter, first_row_of_invoice;
+    gboolean valid, row_fixed, on_first_row_of_invoice, ignore_invoice;
     gchar *id = NULL, *date_opened = NULL, *date_posted = NULL, *due_date = NULL,
         *owner_id = NULL, *date = NULL, *quantity = NULL, *price = NULL;
     GString *running_id;
     guint dummy;
-    gint row = 1;
+    gint row = 1, fixed_for_invoice = 0;
     const gchar* date_format_string = qof_date_format_get_string (qof_date_format_get()); // Get the user set date format string
 
 
@@ -251,12 +251,12 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
     
     // Init control variables
     running_id = g_string_new("");
+    ignore_invoice = FALSE;
     on_first_row_of_invoice = TRUE;
 
     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
@@ -273,27 +273,27 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
         if (on_first_row_of_invoice)
         {
             g_string_assign (running_id, id);
+            first_row_of_invoice = iter;
             
             // Validate the invoice id.
             if (strlen (id) == 0)
             {
-                valid = gtk_list_store_remove (store, &iter);
-                row_deleted = TRUE;
+                // The id in the first row of an invoice is blank, ignore the invoice
+                ignore_invoice = TRUE;
                 g_string_append_printf (info,
-                                        _("ROW %d DELETED, ID_NOT_SET\n"), row);
+                                        _("Row %d: invoice ignored, invoice ID not set.\n"), row);
             }
 
             // Validate customer or vendor.
-            if (!row_deleted && strlen (owner_id) == 0)
+            if (!ignore_invoice && strlen (owner_id) == 0)
             {
-                valid = gtk_list_store_remove (store, &iter);
-                row_deleted = TRUE;
+                ignore_invoice = TRUE;
                 g_string_append_printf (info,
-                                        _("ROW %d DELETED, OWNER_NOT_SET: id=%s\n"),
+                                        _("Row %d: invoice %s ignored, owner not set.\n"),
                                         row, id);
             }
             // Verify that customer or vendor exists.
-            if (!row_deleted)
+            if (!ignore_invoice)
             {
                 if (g_ascii_strcasecmp (type, "BILL") == 0)
                 {
@@ -301,11 +301,10 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
                         (gnc_get_current_book (), owner_id))
                     {
                         // Vendor not found.
-                        valid = gtk_list_store_remove (store, &iter);
-                        row_deleted = TRUE;
+                        ignore_invoice = TRUE;
                         g_string_append_printf (info,
-                                                _("ROW %d DELETED, VENDOR_DOES_NOT_EXIST: id=%s\n"),
-                                                row, id);
+                                                _("Row %d: invoice %s ignored, vendor %s does not exist.\n"),
+                                                row, id, owner_id);
                     }
                 }
                 else if (g_ascii_strcasecmp (type, "INVOICE") == 0)
@@ -314,17 +313,16 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
                         (gnc_get_current_book (), owner_id))
                     {
                         // Customer not found.
-                        valid = gtk_list_store_remove (store, &iter);
-                        row_deleted = TRUE;
+                        ignore_invoice = TRUE;
                         g_string_append_printf (info,
-                                                _("ROW %d DELETED, CUSTOMER_DOES_NOT_EXIST: id=%s\n"),
-                                                row, id);
+                                                _("Row %d: invoice %s ignored, customer %s does not exist.\n"),
+                                                row, id, owner_id);
                     }
                 }
             }
             
             // Verify the date opened.
-            if(!row_deleted && !isDateValid(date_opened))
+            if(!ignore_invoice && !isDateValid(date_opened))
             {
                 // Fix this by using the current date.
                 gchar temp[20];
@@ -339,40 +337,54 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
 
         }
         
-        // Validate and fix item data.
-        if (!row_deleted)
+        // Validate the price.
+        if (strlen (price) == 0)
+        {
+            // No valid price, delete the row
+            ignore_invoice = TRUE;
+            g_string_append_printf (info,
+                                    _("Row %d: invoice %s ignored, price not set.\n"),
+                                    row, id);
+        }
+
+        // Fix item data.
+        if (!ignore_invoice)
         {
-            // Validate the price.
-            if (strlen (price) == 0)
-            {
-                // No valid price, delete the row
-                valid = gtk_list_store_remove (store, &iter);
-                row_deleted = TRUE;
-                g_string_append_printf (info,
-                                        _("ROW %d DELETED, PRICE_NOT_SET: id=%s\n"),
-                                        row, id);
-            }
             
             // Verify the quantity.
-            if (!row_deleted && strlen (quantity) == 0)
+            if (strlen (quantity) == 0)
             {
                 // The quantity is not set, default to 1.
                 gtk_list_store_set (store, &iter, QUANTITY, "1", -1);
                 row_fixed = TRUE;
             }
         }
-
-        if (row_deleted)
+        if (row_fixed) ++fixed_for_invoice;
+        
+        // Move to the next row, skipping all rows of the current invoice if it has a row with an error.
+        if (ignore_invoice)
         {
-            (*deleted)++;
+            // Skip all rows of the current invoice.
+            // Get the next row and its id.
+            iter = first_row_of_invoice;
+            while (valid && g_strcmp0 (id, running_id->str) == 0)
+            {
+                (*deleted)++;
+                valid = gtk_list_store_remove (store, &iter);
+                if (valid) gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ID, &id, -1);
+            }
+            // Fixes for ignored invoices don't count in the statistics.
+            fixed_for_invoice = 0;
+            
+            ignore_invoice = FALSE;
         }
-        else if (row_fixed)
-            (*fixed)++;
-
-        if (!row_deleted)
+        else
+        {
+            // Get the next row and its id.
             valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
-        if (valid) gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ID, &id, -1);
-
+            if (valid) gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ID, &id, -1);
+        }
+        
         // If the id of the next row is blank, it takes the id of the previous row.
         if (valid && strlen(id) == 0)
         {
@@ -384,14 +396,10 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
         if (!valid || (valid && g_strcmp0 (id, running_id->str) != 0))
         {
             on_first_row_of_invoice = TRUE;
+            (*fixed) += fixed_for_invoice;
+            fixed_for_invoice = 0;
         }
-        else
-        {
-            // The next row has the same id, so it is not the first row of an invoice.
-            // But if the first row of an invoice was deleted, the next row is again the first row of the invoice.
-            if (!(row_deleted && on_first_row_of_invoice))
-                on_first_row_of_invoice = FALSE;
-        }
+        else on_first_row_of_invoice = FALSE;
 
         g_free (id);
         g_free (date_opened);
@@ -403,15 +411,10 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
 
         row++;
     }
-
-    if (info && (info->len > 0))
-    {
-        g_string_prepend (info, "\n\n");
-        g_string_prepend (info, _("These rows were deleted:"));
-    }
     
     // Deallocate strings.
     g_string_free (running_id, TRUE);
+
 }
 
 
@@ -539,7 +542,6 @@ gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
                                       gnc_search_customer_on_id (book, owner_id));
             gncInvoiceSetOwner (invoice, owner);
             gncInvoiceSetCurrency (invoice, gncOwnerGetCurrency (owner));	// Set the invoice currency based on the owner
-
             qof_scan_date (date_opened, &day, &month, &year);
             gncInvoiceSetDateOpened (invoice,
                                      gnc_dmy2time64 (day, month, year));

commit 31f234041070db81845501e20dbde35193dce921
Author: Rob Laan <rob.laan at chello.nl>
Date:   Sun Feb 10 12:01:59 2019 +0100

    Fix bug 796987 Import Bills & Invoices: fixing import items only works in special case
    
    Removed dependencies of data field values on previous data rows.
    The import takes the header data for an invoice from the first data row, so passing header data to subsequent rows is not useful.
    And the previous functionality worked only if the id of the current row was blank, which further limited its usefulness.
    
    Import Bills & Invoices: validate header fields only for the first data row of an invoice
    
    As the function takes the header data from the first data row of an invoice,
    it is not necessary to validate the header data in subsequent rows.

diff --git a/gnucash/import-export/bi-import/dialog-bi-import.c b/gnucash/import-export/bi-import/dialog-bi-import.c
index 399e34266..8732160bd 100644
--- a/gnucash/import-export/bi-import/dialog-bi-import.c
+++ b/gnucash/import-export/bi-import/dialog-bi-import.c
@@ -228,10 +228,10 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
                        GString * info, gchar *type)
 {
     GtkTreeIter iter;
-    gboolean valid, row_deleted, row_fixed;
+    gboolean valid, row_deleted, row_fixed, on_first_row_of_invoice;
     gchar *id = NULL, *date_opened = NULL, *date_posted = NULL, *due_date = NULL,
         *owner_id = NULL, *date = NULL, *quantity = NULL, *price = NULL;
-    GString *prev_id, *prev_date_opened, *prev_date_posted, *prev_owner_id, *prev_date;	// needed to fix multi line invoices
+    GString *running_id;
     guint dummy;
     gint row = 1;
     const gchar* date_format_string = qof_date_format_get_string (qof_date_format_get()); // Get the user set date format string
@@ -248,13 +248,10 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
 
     *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 ("");
+    
+    // Init control variables
+    running_id = g_string_new("");
+    on_first_row_of_invoice = TRUE;
 
     valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter);
     while (valid)
@@ -271,184 +268,129 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
                             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
-            valid = gtk_list_store_remove (store, &iter);
-            row_deleted = TRUE;
-            g_string_append_printf (info,
-                                    _("ROW %d DELETED, PRICE_NOT_SET: id=%s\n"),
-                                    row, id);
-        }
-        // TODO: QTY get set to 1 later if field is empty.  Delete this section?
-        else if (strlen (quantity) == 0)
+        
+        //  If this is a row for a new invoice id, validate header values.
+        if (on_first_row_of_invoice)
         {
-            // invalid row (no quantity given)
-            // no fix possible -> delete row
-            valid = gtk_list_store_remove (store, &iter);
-            row_deleted = TRUE;
-            g_string_append_printf (info, _("ROW %d DELETED, QTY_NOT_SET: id=%s\n"),
-                                    row, id);
-        }
-        else
-        {   // TODO: If id is empty get the next one in the series.  Bug 731105
+            g_string_assign (running_id, id);
+            
+            // Validate the invoice id.
             if (strlen (id) == 0)
             {
-                // no invoice id specified
-                if (prev_id->len == 0)
-                {
-                    // cannot fix -> delete row
-                    valid = gtk_list_store_remove (store, &iter);
-                    row_deleted = TRUE;
-                    g_string_append_printf (info,
-                                            _("ROW %d DELETED, ID_NOT_SET\n"), row);
-                }
-                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, "");
+                valid = gtk_list_store_remove (store, &iter);
+                row_deleted = TRUE;
+                g_string_append_printf (info,
+                                        _("ROW %d DELETED, ID_NOT_SET\n"), row);
             }
-        }
-
-        if (!row_deleted)
-        {
-            // the row is valid (price and id are valid)
 
-            if(!isDateValid(date_opened))
+            // Validate customer or vendor.
+            if (!row_deleted && strlen (owner_id) == 0)
             {
-                if (prev_date_opened->len == 0)
-                {
-                    // fix this by using the current date
-                    gchar temp[20];
-                    GDate date;
-                    g_date_clear (&date, 1);
-                    gnc_gdate_set_today (&date);
-                    g_date_strftime (temp, 20, date_format_string, &date);	// Create a user specified date string.
-                    g_string_assign (prev_date_opened, temp);
-                }
-                // 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;
+                valid = gtk_list_store_remove (store, &iter);
+                row_deleted = TRUE;
+                g_string_append_printf (info,
+                                        _("ROW %d DELETED, OWNER_NOT_SET: id=%s\n"),
+                                        row, id);
             }
-            else
+            // Verify that customer or vendor exists.
+            if (!row_deleted)
             {
-                // remember date_opened (to be able to fix multi line invoices)
-                g_string_assign (prev_date_opened, date_opened);
-            }
-
-            // date_opened is valid
-
-             if(!isDateValid(date_posted))
-             {
-                if (prev_date_posted->len == 0)
+                if (g_ascii_strcasecmp (type, "BILL") == 0)
                 {
-                    // this invoice will have to get posted manually
+                    if (!gnc_search_vendor_on_id
+                        (gnc_get_current_book (), owner_id))
+                    {
+                        // Vendor not found.
+                        valid = gtk_list_store_remove (store, &iter);
+                        row_deleted = TRUE;
+                        g_string_append_printf (info,
+                                                _("ROW %d DELETED, VENDOR_DOES_NOT_EXIST: id=%s\n"),
+                                                row, id);
+                    }
                 }
-                else
+                else if (g_ascii_strcasecmp (type, "INVOICE") == 0)
                 {
-                    // multi line invoice => fix it
-                    gtk_list_store_set (store, &iter, DATE_POSTED,
-                                        prev_date_posted->str, -1);
-                    row_fixed = TRUE;
+                    if (!gnc_search_customer_on_id
+                        (gnc_get_current_book (), owner_id))
+                    {
+                        // Customer not found.
+                        valid = gtk_list_store_remove (store, &iter);
+                        row_deleted = TRUE;
+                        g_string_append_printf (info,
+                                                _("ROW %d DELETED, CUSTOMER_DOES_NOT_EXIST: id=%s\n"),
+                                                row, id);
+                    }
                 }
             }
-            else
+            
+            // Verify the date opened.
+            if(!row_deleted && !isDateValid(date_opened))
             {
-                // remember date_opened (to be able to fix multi line invoices)
-                g_string_assign (prev_date_posted, date_posted);
+                // Fix this by using the current date.
+                gchar temp[20];
+                GDate date;
+                g_date_clear (&date, 1);
+                gnc_gdate_set_today (&date);
+                g_date_strftime (temp, 20, date_format_string, &date);    // Create a user specified date string.
+                gtk_list_store_set (store, &iter, DATE_OPENED,
+                                    temp, -1);
+                row_fixed = TRUE;
             }
 
-            // date_posted is valid
-            /*
-            // Check if due date is valid.  Set it to date_posted if not valid or missing.
-            if(!isDateValid(due_date))
+        }
+        
+        // Validate and fix item data.
+        if (!row_deleted)
+        {
+            // Validate the price.
+            if (strlen (price) == 0)
             {
-                gtk_list_store_set (store, &iter, DUE_DATE,
-                                        date_posted, -1);
-                row_fixed = TRUE;
-
+                // No valid price, delete the row
+                valid = gtk_list_store_remove (store, &iter);
+                row_deleted = TRUE;
+                g_string_append_printf (info,
+                                        _("ROW %d DELETED, PRICE_NOT_SET: id=%s\n"),
+                                        row, id);
             }
-
-            // due_date is valid
-            */
-            if (strlen (quantity) == 0)
+            
+            // Verify the quantity.
+            if (!row_deleted && strlen (quantity) == 0)
             {
-                // quantity is unset => set to 1
+                // The quantity is not set, default to 1.
                 gtk_list_store_set (store, &iter, QUANTITY, "1", -1);
                 row_fixed = TRUE;
             }
+        }
 
+        if (row_deleted)
+        {
+            (*deleted)++;
+        }
+        else if (row_fixed)
+            (*fixed)++;
 
-            // quantity is valid
-
-            if (strlen (owner_id) == 0)
-            {
-                if (prev_owner_id->len == 0)
-                {
-                    // no customer given and not fixable => delete row
-                    valid = gtk_list_store_remove (store, &iter);
-                    row_deleted = TRUE;
-                    g_string_append_printf (info,
-                                            _("ROW %d DELETED, OWNER_NOT_SET: id=%s\n"),
-                                            row, 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);
-            }
-            if (g_ascii_strcasecmp (type, "BILL") == 0)
-            {
-                // BILL: check, if vendor exists
-                if (!gnc_search_vendor_on_id
-                        (gnc_get_current_book (), prev_owner_id->str))
-                {
-                    // vendor not found => delete row
-                    valid = gtk_list_store_remove (store, &iter);
-                    row_deleted = TRUE;
-                    g_string_append_printf (info,
-                                            _("ROW %d DELETED, VENDOR_DOES_NOT_EXIST: id=%s\n"),
-                                            row, id);
-                }
-            }
-            else if (g_ascii_strcasecmp (type, "INVOICE") == 0)
-            {
-                // INVOICE: check, if customer exists
-                if (!gnc_search_customer_on_id
-                        (gnc_get_current_book (), prev_owner_id->str))
-                {
-                    // customer not found => delete row
-                    valid = gtk_list_store_remove (store, &iter);
-                    row_deleted = TRUE;
-                    g_string_append_printf (info,
-                                            _("ROW %d DELETED, CUSTOMER_DOES_NOT_EXIST: id=%s\n"),
-                                            row, id);
-                }
-            }
+        if (!row_deleted)
+            valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
+        if (valid) gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ID, &id, -1);
 
-            // owner_id is valid
+        // If the id of the next row is blank, it takes the id of the previous row.
+        if (valid && strlen(id) == 0)
+        {
+            strcpy( id, running_id->str);
+            gtk_list_store_set (store, &iter, ID, id, -1);
+        }
+        
+        // If this row was the last row of the invoice...
+        if (!valid || (valid && g_strcmp0 (id, running_id->str) != 0))
+        {
+            on_first_row_of_invoice = TRUE;
+        }
+        else
+        {
+            // The next row has the same id, so it is not the first row of an invoice.
+            // But if the first row of an invoice was deleted, the next row is again the first row of the invoice.
+            if (!(row_deleted && on_first_row_of_invoice))
+                on_first_row_of_invoice = FALSE;
         }
 
         g_free (id);
@@ -458,37 +400,18 @@ gnc_bi_import_fix_bis (GtkListStore * store, guint * fixed, guint * deleted,
         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)++;
-
-        if (!row_deleted)
-            valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
 
         row++;
     }
 
-    // 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, "\n\n");
         g_string_prepend (info, _("These rows were deleted:"));
     }
+    
+    // Deallocate strings.
+    g_string_free (running_id, TRUE);
 }
 
 
@@ -616,18 +539,10 @@ gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
                                       gnc_search_customer_on_id (book, owner_id));
             gncInvoiceSetOwner (invoice, owner);
             gncInvoiceSetCurrency (invoice, gncOwnerGetCurrency (owner));	// Set the invoice currency based on the owner
-            if (strlen (date_opened) != 0)	// If a date is specified in CSV
-            {
-                // FIXME: Must check for the return value of qof_scan_date!
-                qof_scan_date (date_opened, &day, &month, &year);
-                gncInvoiceSetDateOpened (invoice,
-                                         gnc_dmy2time64 (day, month, year));
-            }
-            else			// If no date in CSV
-            {
-                time64 now = gnc_time (NULL);
-                gncInvoiceSetDateOpened (invoice, now);
-            }
+
+            qof_scan_date (date_opened, &day, &month, &year);
+            gncInvoiceSetDateOpened (invoice,
+                                     gnc_dmy2time64 (day, month, year));
             gncInvoiceSetBillingID (invoice, billing_id ? billing_id : "");
             notes = un_escape(notes);
             gncInvoiceSetNotes (invoice, notes ? notes : "");

commit eab9f19beff5da7e635406caa2d6f9bb936dc6c5
Author: Rob Laan <rob.laan at chello.nl>
Date:   Sun Jan 20 18:56:18 2019 +0100

    Import Bills & Invoices: get header data consistently from the first row of an invoice/bill
    
    Previously, the posting data would be from the last row of an invoice, other header data from the first row.

diff --git a/gnucash/import-export/bi-import/dialog-bi-import.c b/gnucash/import-export/bi-import/dialog-bi-import.c
index 3ecb42e80..399e34266 100644
--- a/gnucash/import-export/bi-import/dialog-bi-import.c
+++ b/gnucash/import-export/bi-import/dialog-bi-import.c
@@ -507,8 +507,8 @@ gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
                           gchar * type, gchar * open_mode, GString * info,
                           GtkWindow *parent)
 {
-    gboolean valid;
-    GtkTreeIter iter;
+    gboolean valid, on_first_row_of_invoice;
+    GtkTreeIter iter, first_row_of_invoice;
     gchar *id = NULL, *date_opened = NULL, *owner_id = NULL, *billing_id = NULL, *notes = NULL;
     gchar *date = NULL, *desc = NULL, *action = NULL, *account = NULL, *quantity = NULL,
           *price = NULL, *disc_type = NULL, *disc_how = NULL, *discount = NULL, *taxable = NULL,
@@ -526,9 +526,9 @@ gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
     GtkWidget *dialog;
     time64 today;
     InvoiceWindow *iw;
-    gchar *new_id = NULL;
     gint64 denom = 0;
     gnc_commodity *currency;
+    GString *running_id;
 
     // these arguments are needed
     g_return_if_fail (store && book);
@@ -546,7 +546,9 @@ gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
 
     invoice = NULL;
     update = NO;
-
+    on_first_row_of_invoice = TRUE;
+    running_id = g_string_new("");
+    
     valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter);
     while (valid)
     {
@@ -582,6 +584,13 @@ gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
         // 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 (on_first_row_of_invoice)
+        {
+            g_string_assign(running_id, id);
+            first_row_of_invoice = iter;
+        }
+        
         if (g_ascii_strcasecmp (type, "BILL") == 0)
             invoice = gnc_search_bill_on_id (book, id);
         else if (g_ascii_strcasecmp (type, "INVOICE") == 0)
@@ -757,14 +766,27 @@ gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
         gncEntryCommitEdit(entry);
         valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
         // handle auto posting of invoices
-        
-        new_id = NULL;
        
         if (valid)
-            gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ID, &new_id, -1);
-        if (g_strcmp0 (id, new_id) != 0)
+            gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ID, &id, -1);
+        else
+            id = NULL;
+        
+        if (g_strcmp0 (id, running_id->str) == 0) // The next row is for the same invoice.
         {
-            // the next invoice id is different => try to autopost this invoice
+            on_first_row_of_invoice = FALSE;
+        }
+        else // The next row is for a new invoice; try to post the invoice.
+        {
+            // Use posting values from the first row of this invoice.
+            gtk_tree_model_get (GTK_TREE_MODEL (store), &first_row_of_invoice,
+                                ID, &id,
+                                DATE_POSTED, &date_posted,
+                                DUE_DATE, &due_date,
+                                ACCOUNT_POSTED, &account_posted,
+                                MEMO_POSTED, &memo_posted,
+                                ACCU_SPLITS, &accumulatesplits, -1);
+
             if (strlen(date_posted) != 0)
             {
                 // autopost this invoice
@@ -832,20 +854,21 @@ gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
             {
                 PWARN("Invoice %s is NOT marked for posting",id);
             }
+        
+            // open new bill / invoice in a tab, if requested
+            if (g_ascii_strcasecmp(open_mode, "ALL") == 0
+                    || (g_ascii_strcasecmp(open_mode, "NOT_POSTED") == 0
+                        && strlen(date_posted) == 0))
+            {
+                iw =  gnc_ui_invoice_edit (parent, invoice);
+                gnc_plugin_page_invoice_new (iw);
+            }
+            
+            // The next row will be for a new invoice.
+            on_first_row_of_invoice = TRUE;
         }
-        // open new bill / invoice in a tab, if requested
-        if (g_ascii_strcasecmp(open_mode, "ALL") == 0
-                || (g_ascii_strcasecmp(open_mode, "NOT_POSTED") == 0
-                    && strlen(date_posted) == 0))
-        {
-            iw =  gnc_ui_invoice_edit (parent, invoice);
-            gnc_plugin_page_invoice_new (iw);
-        }
-
-
     }
     // cleanup
-    g_free (new_id);
     g_free (id);
     g_free (date_opened);
     g_free (owner_id);
@@ -869,7 +892,7 @@ gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
     g_free (memo_posted);
     g_free (accumulatesplits);
     
-
+    g_string_free (running_id, TRUE);
 }
 
 /* Change any escaped quotes ("") to (")

commit 57a554d606b7893f96ae7f15d8da15e08726ac05
Author: Rob Laan <rob.laan at chello.nl>
Date:   Sun Jan 20 16:56:01 2019 +0100

    Fix bug 797024 Import Bills & Invoices: import matches csv data rows with too few separators

diff --git a/gnucash/import-export/bi-import/dialog-bi-import-gui.c b/gnucash/import-export/bi-import/dialog-bi-import-gui.c
index ef35c74cd..66e362f58 100644
--- a/gnucash/import-export/bi-import/dialog-bi-import-gui.c
+++ b/gnucash/import-export/bi-import/dialog-bi-import-gui.c
@@ -124,7 +124,7 @@ gnc_plugin_bi_import_showGUI (GtkWindow *parent)
 
     gui->book = gnc_get_current_book();
 
-    gui->regexp = g_string_new ( "^(\\x{FEFF})?(?<id>[^;]*);(?<date_opened>[^;]*);(?<owner_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>[^;]*)$");
+    gui->regexp = g_string_new ( "^(\\x{FEFF})?(?<id>[^;]*);(?<date_opened>[^;]*);(?<owner_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>[^;]*)$");
 
     // create model and bind to view
     gui->store = gtk_list_store_new (N_COLUMNS,
@@ -309,7 +309,7 @@ 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, "^(\\x{FEFF})?(?<id>[^;]*);(?<date_opened>[^;]*);(?<owner_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>[^;]*)$");
+    g_string_assign (gui->regexp, "^(\\x{FEFF})?(?<id>[^;]*);(?<date_opened>[^;]*);(?<owner_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>[^;]*)$");
     gnc_bi_import_gui_filenameChanged_cb (gui->entryFilename, gui);
 }
 
@@ -319,7 +319,7 @@ 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, "^(\\x{FEFF})?(?<id>[^,]*),(?<date_opened>[^,]*),(?<owner_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>[^,]*)$");
+    g_string_assign (gui->regexp, "^(\\x{FEFF})?(?<id>[^,]*),(?<date_opened>[^,]*),(?<owner_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>[^,]*)$");
     gnc_bi_import_gui_filenameChanged_cb (gui->entryFilename, gui);
 }
 



Summary of changes:
 .../import-export/bi-import/dialog-bi-import-gui.c |  12 +-
 gnucash/import-export/bi-import/dialog-bi-import.c | 844 +++++++++++----------
 gnucash/import-export/bi-import/dialog-bi-import.h |   3 +-
 3 files changed, 471 insertions(+), 388 deletions(-)



More information about the gnucash-changes mailing list