gnucash master: Multiple changes pushed

John Ralls jralls at code.gnucash.org
Sun Jun 24 14:11:26 EDT 2018


Updated	 via  https://github.com/Gnucash/gnucash/commit/274f0fd5 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/5d80a52e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7e4f9a44 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/22fb8511 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/ddd06e69 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1e6627c4 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/b74cc7c4 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a438a595 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/010dd04e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/ae4b0bd8 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3af9acec (commit)
	 via  https://github.com/Gnucash/gnucash/commit/92ea3ba8 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/f144a8de (commit)
	 via  https://github.com/Gnucash/gnucash/commit/696277bb (commit)
	 via  https://github.com/Gnucash/gnucash/commit/10e20f97 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/32e79948 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7a33c6c0 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e1288360 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3f9bb7a1 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/4485966b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a2983935 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/91af85ad (commit)
	 via  https://github.com/Gnucash/gnucash/commit/d906ac41 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a9f0fe2f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1e3a4450 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/4aaa18ea (commit)
	 via  https://github.com/Gnucash/gnucash/commit/9b7544a7 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/c1103a3a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/186ac71f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a78e8b10 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/6a62b8db (commit)
	 via  https://github.com/Gnucash/gnucash/commit/62b5c813 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1b4a2acb (commit)
	 via  https://github.com/Gnucash/gnucash/commit/01a426bf (commit)
	 via  https://github.com/Gnucash/gnucash/commit/02bb981d (commit)
	 via  https://github.com/Gnucash/gnucash/commit/f1823c52 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/0461eb43 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/b1a165eb (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1801fed4 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7ce8c9d3 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/d6de324b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1f8f6817 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/9f1bfddc (commit)
	 via  https://github.com/Gnucash/gnucash/commit/edd439a0 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/55400160 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/0efe32ea (commit)
	 via  https://github.com/Gnucash/gnucash/commit/754c0478 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/be1ebb9d (commit)
	 via  https://github.com/Gnucash/gnucash/commit/66488bbb (commit)
	 via  https://github.com/Gnucash/gnucash/commit/ffd20b2e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/8cae602e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/2e4e18e2 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7de68cef (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1fc5634c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/6c03d07e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/87f4791f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/9c7fa77d (commit)
	 via  https://github.com/Gnucash/gnucash/commit/5d3ae6c6 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/ffe6044c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/c8861d46 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/91795052 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e4407dee (commit)
	 via  https://github.com/Gnucash/gnucash/commit/aa4dfb0e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/bb37adc3 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/81303b41 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/da1d1b9a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7918c031 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/74621992 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/b30f4d7c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/af007125 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/9e6760f7 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/5f53e292 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/64837820 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/2384af60 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a73f9123 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1f14d0f6 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/00e6ccdd (commit)
	 via  https://github.com/Gnucash/gnucash/commit/6f0a3c43 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/dc713592 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/cdcb230a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e3e1464a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/690ef626 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/8cfa078b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/b431b648 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/b787baaa (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1641c422 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/ccf3ebda (commit)
	 via  https://github.com/Gnucash/gnucash/commit/da0160ad (commit)
	 via  https://github.com/Gnucash/gnucash/commit/848e3da1 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/324cfeb3 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/0b10b4b9 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/b4b16be3 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a97f9faf (commit)
	 via  https://github.com/Gnucash/gnucash/commit/06808469 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/aeb25313 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/5093a8fb (commit)
	 via  https://github.com/Gnucash/gnucash/commit/0dc9a115 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/5532de04 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7927597b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/39e97604 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/03b57e51 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/879ec75f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e9535bfa (commit)
	 via  https://github.com/Gnucash/gnucash/commit/aafd46a4 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/98964f7a (commit)
	from  https://github.com/Gnucash/gnucash/commit/eb67baba (commit)



commit 274f0fd5857d521c3059c07987ac91cd6f55122c
Merge: eb67bab 5d80a52
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Jun 24 11:11:59 2018 -0700

    Merge branch 'maint'


commit 5d80a52eaa3af1135e6854a3d6326df4a3f73268
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Jun 24 11:09:08 2018 -0700

    Release GnuCash 3.2

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8207055..5e10a09 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -12,13 +12,13 @@ enable_testing()
 
 # Version number of gnucash
 set (GNUCASH_MAJOR_VERSION 3)
-set (GNUCASH_MINOR_VERSION 1)
+set (GNUCASH_MINOR_VERSION 2)
 set (VERSION "${GNUCASH_MAJOR_VERSION}.${GNUCASH_MINOR_VERSION}")
 set (GNUCASH_LATEST_STABLE_SERIES 3.x)
 
 set (PACKAGE gnucash)
 set (PACKAGE_NAME GnuCash)
-set (PACKAGE_VERSION 3.1)
+set (PACKAGE_VERSION 3.2)
 set (PACKAGE_BUGREPORT gnucash-devel at gnucash.org)
 set (PACKAGE_TARNAME ${PACKAGE})
 set (PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}")
diff --git a/NEWS b/NEWS
index 811fe2f..e960193 100644
--- a/NEWS
+++ b/NEWS
@@ -1,7 +1,151 @@
 Version history:
 ------- -------
-3.1    - 29 April 2018
-The second release of the 3.0 stable series.
+3.2 - 25 June 2018
+The third release of the 3.x stable series.
+
+The following bugs have been fixed:
+    Bug 787401 - Test Report System - Report Definition.
+    Bug 794617 - Can't compile with -DWITH_GNUCASH=NO due to scm-gnome-utils.
+    Bug 795101 - Scroll Bar in Reconcile Window Floats in and covers the check
+        boxes.
+    Bug 795247 - datepicker broken in Persian.
+             GnuCash passes dates as integer y/m/d without using locale-specific
+             formats, so we need to strip out 'E' and 'O' from the format when
+             scanning dates or determining separators in gnc-date. None of '-',
+             'E', or 'O' are supported by boost (and '-' causes errors), so
+             strip them out from formatters in gnc-datetime as well.
+    Bug 795253 - Have problems input Chinese.
+    Bug 795272 - QIF importer causes application crash if action is invalid.
+    Bug 795276 - Invalid date on price stops file from being parsed.
+    Bug 795362 - Special variable "i" not parsed in function calls.
+             Due to balance tests with insane random values.
+    Bug 795471 - Impossible to Edit Budget Unless Maximized.
+    Bug 795519 - Credit card payment after reconciliation.
+    Bug 795666 - Backslash '\' in Description field spoils CSV Import without
+        helpful error message.
+    Bug 795831 - When read only threshold set, dates are silently changed.
+             Display a message box informing the user of the change.
+    Bug 795944 - Cannot store change to Business Suppliers data.
+    Bug 796079 - Repeatable Crash in Tax Report Options.
+    Bug 796081 - Tax Schedule Report - An error occurred while running the report.
+    Bug 796083 - Reconcile Selection Doesn't Work Anymore.
+    Bug 796117 - Connecting 3.1 to an existing mysql db drops all data
+             Provide a backup recovery function that instead of dropping
+             primaries and restoring backups merges the primaries and
+             backups. This should handle a worst-case safe-save failure where
+             the backup tables don't have a complete set of rows for some
+             reason.
+    Bug 796256 - Main Window stays hidden when starting after closing main
+        window while minimized.
+    Bug 796369 - Notes lost or perhaps just not displaying when using SQLite
+        backend.
+             This bug caused data loss if you saved your SQLite3 database to a
+             different file or database.  The problem is that in SQLite3 (though
+             not in MySQL or PgSQL) the subquery ((SELECT DISTINCT guid FROM
+             transactions)) (note the double parentheses) returns only the first
+             guid in the subquery's results.  Some transactions are loaded by
+             special queries and those queries are also used to retrieve the
+             transaction's slots so they weren't affected.
+    Bug 796398 - Restrict accelerator keys to valid date range.
+    Bug 796409 - Incorrect Current Value for Stocks.
+             Missed calculating the value in the register summary bar.
+    Bug 796423 - Cannot Input Chinese, seems does not work with other IME too.
+             Toggles not in view with all rows selected weren't being redrawn.
+    Bug 796484 - csv import: iostream error
+             Unfortunately it turns out that we can't use filestreams because
+             they can't take path arguments containing Unicode on Windows.
+    Bug 796527 - invalid currency on scheduled transactions.
+             * Don't even check for price/exchange rate on template
+               transactions, there's no point.
+             * Check all split commodities are valid, abort transaction
+               creation if not.
+             * If the template transaction's currency isn't used by any of the
+               splits set the new transaction's currency to the first-found
+               currency if there is one, otherwise to the first-found commodity.
+    Bug 796537 - Transaction Report cannot sort by "num".
+    Bug 796586 - QIF import incorrectly converts unicode characters from UTF8
+        encoded file.
+    Bug 796595 - QIF Import Select Account button to add a new account is
+        labled gnc-account-new but should be New.
+    Bug 796600 - stock split cash-in-lieu income/asset labels backwards.
+    Bug 796614 - Reconciliation report contains incorrect transactions.
+    Bug 796638 - configuration not properly saved for CSV transactions import
+        form.
+
+
+Other repairs or enhancements not marked as bugs:
+    Transaction report improvements:
+      * Performance: Do all filtering ops before sorting.
+      * Move the options summary before the subtotals table.
+      * New unit tests
+      * Enable computing averages in subtotal grid.
+      * Generate the subtotal grid only if the primary sort key enables
+        subtotals.
+      * Fix subtotal grid to support multiple commodities.
+      * New filters.
+      * Much cleaning and refactoring of the internals.
+    Cache the num-split-action book option to avoid thousands of KVP queries
+      for the same value when loading a register.
+    Create a static string for book option KVPs to save an extra malloc/free
+      on old compilers without small-string optimization.
+    Add tooltip support for register cells.
+    New unit tests for the balance sheet report and invoices.
+    Consolidate charting code into a single module.
+    Removed an incomplete and abandoned C implementation of QIF import.
+    Restored libsecret support that had been left out of CMake.
+    Replace old-style html style attributes with css.
+    Modify emitted html to be parseable by the Guile SXML module for testing.
+    Remove support for long-gone gtkhtml renderer.
+    Clean up code generating html tables.
+    Ensure full precision of doubles is saved to SQL.
+      std::iostream's operator<<(double) uses only 6 digits of precision by
+      default.
+    Make float database operations more consistent, working around dbd-sqlite3's
+      failure to support doubles (dbd-sqlite3, not sqlite3 itself).
+    Prevent crash in gnc-tree-view.c: When getting information from the state
+      file, protect against the key not having a '_' which is used to split the
+      string.
+    Add the full account name to the saved register settings
+      If you need to delete the layout for a register in the settings file,
+      the only thing identifying it is the account guid. To make it easier
+      for humans, add the full account name also.
+    When register pages are restored it uses the full account name.
+      When register pages are restored, the account is found from the full
+      name so if the separator changes it will fail. Instead, also save the
+      account guid and use that as default to find the account falling back
+      to the full name.
+    Fix misplaced try block that caused unhandled exception if year out of
+      range.
+    Fix permanent storage of vendor details: Since the data types did not match
+      for the billterms and taxtable, those references/guids were not saved to
+      the database.
+    Update invoice reports to use totals calculate by gncInvoice. This should
+      give a consistent representation of invoice data across the application.
+    Ensure gncEntry rounding is consistent. Internally calculated values in the
+      entry are never rounded. Consumers of gncEntry's calculated values can
+      request them either rounded or not. Next use a pragmatical approach for
+      calculating values on invoices based on the entry values: do the rounding
+      such that we never create an unbalanced transaction while posting.
+      That means:
+      * round each entry's net value before summing them in net total
+      * accumulate all tax totals on invoice level per tax account before
+        rounding
+      and round before before summing them in a global tax total
+      Hopefully this will catch a few more rounding issues in this area.
+      A complete solution can only offered if we allow users to manually correct
+      tax entries. This requires changes to user interface and data format so
+      that's not going to happen in gnucash 3.x.
+    Use Scheme SRFI-64 test framework for new Scheme unit tests.
+    New compilers (gcc-8.0 and Xcode 9) bring new warnings, so several fixups to
+      mollify them.
+    Lowercase all cmake commands for better readability.
+    Metadate migration (2.6.x->3.x) fixes for Windows.
+    Ensure timezone is set correctly in FreeBSD.
+
+Updated Translations: Dutch
+
+3.1 - 29 April 2018
+The second release of the 3.x stable series.
 
 The following bugs have been fixed:
     Bug 118391 - Long currency names untranslated
@@ -153,8 +297,8 @@ Known Problems:
     Invalid input into a formula (Debit/Credit) field is inconsistent
         depending on the input. Bug 795614.
 
-3.0    - 1 April 2018
-The first release of the 3.0 stable series.
+3.0 - 1 April 2018
+The first release of the 3.x stable series.
 New Features for Users:
     The headline item for this release is that GnuCash now uses the
         Gtk+-3.0 Toolkit and the WebKit2Gtk API. This change was forced

commit 7e4f9a444ff7dcbf75955bd361663a2519c77c02
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Jun 23 22:00:34 2018 -0700

    Add some files missing from the diat so distcheck passes.

diff --git a/gnucash/report/business-reports/CMakeLists.txt b/gnucash/report/business-reports/CMakeLists.txt
index 9217934..c4c5017 100644
--- a/gnucash/report/business-reports/CMakeLists.txt
+++ b/gnucash/report/business-reports/CMakeLists.txt
@@ -47,4 +47,7 @@ gnc_add_scheme_targets(scm-business-reports
 
 install(FILES ${business_reports_DATA} DESTINATION ${SCHEME_INSTALLED_SOURCE_DIR}/gnucash/report)
 
-set_dist_list(business_reports_DIST CMakeLists.txt ${business_reports_SCHEME} ${business_reports_DATA})
+set_local_dist(business_reports_DIST_local CMakeLists.txt
+  ${business_reports_SCHEME} ${business_reports_DATA})
+set(business_reports_DIST ${business_reports_DIST_local}
+  ${test_business_reports_DIST} PARENT_SCOPE)
diff --git a/libgnucash/app-utils/test/CMakeLists.txt b/libgnucash/app-utils/test/CMakeLists.txt
index 8995b45..d3a55cc 100644
--- a/libgnucash/app-utils/test/CMakeLists.txt
+++ b/libgnucash/app-utils/test/CMakeLists.txt
@@ -82,6 +82,7 @@ set_dist_list(test_app_utils_DIST
   test-scm-query-string.cpp
   test-sx.cpp
   test-c-interface.scm
+  test-date-utilities.scm
   ${test_app_utils_scheme_SOURCES}
   ${test_app_utils_SOURCES}
 )

commit 22fb85113aa1ea4cfdeea184e4707326e788cd20
Merge: ddd06e6 010dd04
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Jun 23 17:46:25 2018 -0700

    Merge Chris Lam's  'maint-fix-796614' into maint.


commit ddd06e69e8326f9236c3532465cce0a7b27ef789
Merge: 1e6627c 91af85a
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Jun 23 17:38:53 2018 -0700

    Merge Bob Fewell's 'reg-tooltips' into maint.


commit 1e6627c4c55dd824bf48e3e2893d973424120b8c
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Jun 23 17:35:42 2018 -0700

    [DBI Backend] Init and finalize the backend in setup and teardown.
    
    Xcode 9's clang creates a separate dbi_instance for each test and so
    each one needs to be initialized during its setup and finalized at
    teardown.

diff --git a/libgnucash/backend/dbi/test/test-backend-dbi-basic.cpp b/libgnucash/backend/dbi/test/test-backend-dbi-basic.cpp
index 8c0d75e..7fb08a9 100644
--- a/libgnucash/backend/dbi/test/test-backend-dbi-basic.cpp
+++ b/libgnucash/backend/dbi/test/test-backend-dbi-basic.cpp
@@ -53,6 +53,7 @@ extern "C"
 }
 /* For test_conn_index_functions */
 #include "../gnc-backend-dbi.hpp"
+#include "../gnc-backend-dbi.h"
 extern "C"
 {
 #include <unittest-support.h>
@@ -89,6 +90,7 @@ static void
 setup (Fixture* fixture, gconstpointer pData)
 {
     gchar* url = (gchar*)pData;
+    gnc_module_init_backend_dbi();
     fixture->session = qof_session_new ();
     /* When running distcheck the source directory is read-only, which
      * prevents creating the lock file. Force the session to get
@@ -109,7 +111,7 @@ setup (Fixture* fixture, gconstpointer pData)
 static void
 setup_memory (Fixture* fixture, gconstpointer pData)
 {
-    QofSession* session = qof_session_new ();
+    QofSession* session = nullptr;
     gchar* url = (gchar*)pData;
     QofBook* book;
     Account* root, *acct1, *acct2;
@@ -118,6 +120,7 @@ setup_memory (Fixture* fixture, gconstpointer pData)
     gnc_commodity_table* table;
     gnc_commodity* currency;
 
+    gnc_module_init_backend_dbi();
     session = qof_session_new ();
     book = qof_session_get_book (session);
     root = gnc_book_get_root_account (book);
@@ -166,6 +169,7 @@ setup_memory (Fixture* fixture, gconstpointer pData)
 static void
 setup_business (Fixture* fixture, gconstpointer pData)
 {
+    gnc_module_init_backend_dbi ();
     QofSession* session = qof_session_new ();
     gchar* url = (gchar*)pData;
     QofBook* book = qof_session_get_book (session);
@@ -349,6 +353,7 @@ teardown (Fixture* fixture, gconstpointer pData)
     g_free (lockfile);
     g_slist_free_full (fixture->hdlrs, test_free_log_handler);
     test_clear_error_list ();
+    gnc_module_finalize_backend_dbi();
 }
 
 #if 0 //temporarily disable test pending refactor.

commit b74cc7c4e6b51685a5ac5092f194ead22f195760
Merge: a438a59 7a33c6c
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Sat Jun 23 17:39:29 2018 +0200

    Merge branch 'bug795471' of https://github.com/Bob-IT/gnucash into maint


commit a438a59513c703e320189f542b598d84ef21eb41
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Sat Jun 23 13:15:38 2018 +0200

    Bug 795944 - Cannot store change to Business Suppliers data
    
    The underlying problem was that the vendor object remained in infant state
    That confused the backend code so it used an sql INSERT statement instead
    of an UPDATE statement to write back the changes. As the object already
    existed in the db this would fail.
    The fix is to ensure the object doesn't remain in infant state during
    sql loading. See the bug report for a more detailed explanation.

diff --git a/libgnucash/backend/sql/gnc-sql-column-table-entry.hpp b/libgnucash/backend/sql/gnc-sql-column-table-entry.hpp
index ce4dfe2..112338b 100644
--- a/libgnucash/backend/sql/gnc-sql-column-table-entry.hpp
+++ b/libgnucash/backend/sql/gnc-sql-column-table-entry.hpp
@@ -544,9 +544,19 @@ void set_parameter(T object, P item, QofSetterFunc setter)
 template <typename T, typename P>
 void set_parameter(T object, P item, const char* property)
 {
-    qof_instance_increase_editlevel(object);
+    // Properly use qof_begin_edit and qof_commit_edit{_part2}
+    // here. This is needed to reset the infant state of objects
+    // when loading them initially from sql. Failing to do so
+    // could prevent future editing of these objects
+    // Example of this is https://bugzilla.gnome.org/show_bug.cgi?id=795944
+    qof_begin_edit(QOF_INSTANCE(object));
     g_object_set(object, property, item, nullptr);
-    qof_instance_decrease_editlevel(object);
+    if (!qof_commit_edit(QOF_INSTANCE(object))) return;
+    // FIXME I can't use object specific callbacks in generic code
+    // so for now these will silently fail. As the GObject based method
+    // of setting qof objects should go away eventually I won't bother
+    // finding a proper solution for this.
+    qof_commit_edit_part2(QOF_INSTANCE(object), nullptr, nullptr, nullptr);
 };
 
 /**

commit 010dd04e826adc8b24636eac61dab302685cb8e5
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Mon Jun 18 14:46:45 2018 +0800

    [TR] move options-summary to appear above subtotal-table
    
    It seems more fitting that the order of items is:
    - title
    - options summary
    - subtotal table
    - main table

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 45dab5a..f59bb52 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -2041,6 +2041,11 @@ be excluded from periodic reporting.")
                             (qof-print-date begindate)
                             (qof-print-date enddate)))))
 
+                (if (eq? infobox-display 'always)
+                    (gnc:html-document-add-object!
+                     document
+                     (gnc:html-render-options-changed options)))
+
                 (if (and (opt-val gnc:pagename-display optname-grid)
                          (if (memq primary-key DATE-SORTING-TYPES)
                              (keylist-get-info date-subtotal-list primary-date-subtotal 'renderer-fn)
@@ -2055,11 +2060,6 @@ be excluded from periodic reporting.")
                       (gnc:html-document-add-object!
                        document (grid->html-table grid list-of-rows list-of-cols))))
 
-                (if (eq? infobox-display 'always)
-                    (gnc:html-document-add-object!
-                     document
-                     (gnc:html-render-options-changed options)))
-
                 (gnc:html-document-add-object! document table)))))
 
     (gnc:report-finished)

commit ae4b0bd871a95d47582b54064eac125230fee269
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Mon Jun 18 10:37:28 2018 +0800

    [TR] apply custom-sort after filtering.
    
    As a follow on to last commit, if a large datafile is subject to a
    reconciled date filter, the initial QofQuery date matcher would be
    skipped, causing a large number of splits sent for custom
    sorting prior to filtering.
    
    It will always be more efficient that filtering is applied
    first. Therefore custom sorting should be applied after filtering.

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 0a7a4a1..45dab5a 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -1977,12 +1977,6 @@ be excluded from periodic reporting.")
 
           (qof-query-destroy query)
 
-          (if custom-sort?
-              (begin
-                (set! splits (stable-sort! splits date-comparator?))
-                (set! splits (stable-sort! splits secondary-comparator?))
-                (set! splits (stable-sort! splits primary-comparator?))))
-
           ;; Combined Filter:
           ;; - include/exclude using split->date according to date options
           ;; - include/exclude splits to/from selected accounts
@@ -2013,6 +2007,11 @@ be excluded from periodic reporting.")
                                  )))
                         splits))
 
+          (when custom-sort?
+            (set! splits (stable-sort! splits date-comparator?))
+            (set! splits (stable-sort! splits secondary-comparator?))
+            (set! splits (stable-sort! splits primary-comparator?)))
+
           (if (null? splits)
 
               ;; error condition: no splits found

commit 3af9acec998403216873d55790428039e5cdb8bf
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Mon Jun 18 10:36:11 2018 +0800

    Bug 796614 - Reconciliation report contains incorrect transactions
    
    This commit modifies the date filter for reconciliation report,
    ensuring only splits whose reconciled dates match report options.
    
    If a split is not yet reconciled, include it anyway.
    
    In a large datafile with a narrow reconciled date range, the
    datefilter is likely to be the filter with highest frequency of #f
    therefore it should be prioritised in the combined filter.
    
    I chose to add another optional keyword instead of reusing the
    existing #:custom-split-filter, because we need to detect when to
    override the QofQuery date filter.

diff --git a/gnucash/report/standard-reports/test/test-transaction.scm b/gnucash/report/standard-reports/test/test-transaction.scm
index 1418987..56f6ffc 100644
--- a/gnucash/report/standard-reports/test/test-transaction.scm
+++ b/gnucash/report/standard-reports/test/test-transaction.scm
@@ -901,13 +901,13 @@
       (set-option! options "General" "End Date" (cons 'absolute (gnc-dmy2time64 31 03 1970)))
       (let ((sxml (options->sxml options "filter reconcile date")))
         (test-equal "test reconciled amounts = $8"
-          (list "Total For Reconciled" "-$8.00")
+          (list "Total For Reconciled" "$8.00")
           (get-row-col sxml 3 #f))
         (test-equal "test cleared amounts = $29"
           (list "Total For Cleared" "$29.00")
           (get-row-col sxml 6 #f))
-        (test-equal "test unreconciled amounts = -$31"
-          (list "Total For Unreconciled" "-$31.00")
+        (test-equal "test unreconciled amounts = $31"
+          (list "Total For Unreconciled" "$31.00")
           (get-row-col sxml 11 #f))
         sxml)
       )))
diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 11c3a74..0a7a4a1 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -16,6 +16,7 @@
 ;;   and enable multiple data columns
 ;; - add support for indenting for better grouping
 ;; - add defaults suitable for a reconciliation report
+;;   including alternative date filtering strategy
 ;; - add subtotal summary grid
 ;; - by default, exclude closing transactions from the report
 ;;
@@ -446,10 +447,44 @@ Credit Card, and Income accounts."))
   (gnc:option-set-value (gnc:lookup-option options gnc:pagename-general optname-startdate) (cons 'relative 'start-prev-quarter))
   (gnc:option-set-value (gnc:lookup-option options gnc:pagename-general optname-enddate)   (cons 'relative 'today))
   (gnc:option-set-value (gnc:lookup-option options gnc:pagename-display (N_ "Reconciled Date")) #t)
-  (gnc:option-set-value (gnc:lookup-option options gnc:pagename-display (N_ "Running Balance")) #t)
+  (gnc:option-set-value (gnc:lookup-option options gnc:pagename-display (N_ "Running Balance")) #f)
   (gnc:option-set-value (gnc:lookup-option options gnc:pagename-display (N_ "Memo")) #f)
+  (gnc:option-make-internal! options gnc:pagename-display "Running Balance")
   options)
 
+(define reconcile-report-instructions
+  (gnc:make-html-text
+   (_ "The reconcile report is designed to be similar to the formal reconciliation tool.
+Please select the account from Report Options. Please note the dates specified in the options
+will apply to the Reconciliation Date.")
+   (gnc:html-markup-br)
+   (gnc:html-markup-br)))
+
+;; if split is reconciled, retrieve its reconciled date; if not yet reconciled, return #f
+(define (split->reconcile-date split)
+  (and (char=? (xaccSplitGetReconcile split) #\y)
+       (xaccSplitGetDateReconciled split)))
+
+(define (reconcile-report-calculated-cells options)
+  (define (opt-val section name)
+    (gnc:option-value (gnc:lookup-option options section name)))
+  (letrec
+      ((split-amount (lambda (s) (if (gnc:split-voided? s)
+                                     (xaccSplitVoidFormerAmount s)
+                                     (xaccSplitGetAmount s))))
+       (split-currency (lambda (s) (xaccAccountGetCommodity (xaccSplitGetAccount s))))
+       (amount (lambda (s) (gnc:make-gnc-monetary (split-currency s) (split-amount s))))
+       (debit-amount (lambda (s) (and (positive? (split-amount s))
+                                      (amount s))))
+       (credit-amount (lambda (s) (and (not (positive? (split-amount s)))
+                                       (gnc:monetary-neg (amount s))))))
+    ;; similar to default-calculated-cells but disable dual-subtotals.
+    (list (vector (_ "Funds In")
+                  debit-amount #f #t #f
+                  (const ""))
+          (vector (_ "Funds Out")
+                  credit-amount #f #t #f
+                  (const "")))))
 ;;
 ;; Default Transaction Report
 ;;
@@ -1047,11 +1082,11 @@ be excluded from periodic reporting.")
                (add-if (column-uses? 'reconciled-date)
                        (vector (_ "Reconciled Date")
                                (lambda (split transaction-row?)
-                                 (gnc:make-html-table-cell/markup
-                                  "date-cell"
-                                  (if (eqv? (xaccSplitGetReconcile split) #\y)
-                                      (qof-print-date (xaccSplitGetDateReconciled split))
-                                      "")))))
+                                 (let ((reconcile-date (split->reconcile-date split)))
+                                   (and reconcile-date
+                                        (gnc:make-html-table-cell/markup
+                                         "date-cell"
+                                         (qof-print-date reconcile-date)))))))
 
                (add-if (column-uses? 'num)
                        (vector (if (and BOOK-SPLIT-ACTION
@@ -1764,13 +1799,20 @@ be excluded from periodic reporting.")
 ;; Here comes the renderer function for this report.
 
 
-(define* (trep-renderer report-obj #:key custom-calculated-cells empty-report-message custom-split-filter)
+(define* (trep-renderer report-obj #:key custom-calculated-cells empty-report-message
+                        custom-split-filter split->date split->date-include-false?)
   ;; the trep-renderer is a define* function which, at minimum, takes the report object
   ;;
   ;; the optional arguments are:
   ;; #:custom-calculated-cells - a list of vectors to define customized data columns
-  ;; #:empty-report-message - a str which is displayed at the initial report opening
+  ;; #:empty-report-message - a str or html-object which is displayed at the initial report opening
   ;; #:custom-split-filter - a split->bool function to add to the split filter
+  ;; #:split->date - a split->time64 which overrides the default posted date filter
+  ;;     (see reconcile report)
+  ;; #:split->date-include-false? - addendum to above, specifies filter behaviour if
+  ;;     split->date returns #f. useful to include unreconciled splits in reconcile
+  ;;     report. it can be useful for alternative date filtering, e.g. filter by
+  ;;     transaction->invoice->payment date.
 
   (define options (gnc:report-options report-obj))
   (define (opt-val section name) (gnc:option-value (gnc:lookup-option options section name)))
@@ -1908,7 +1950,8 @@ be excluded from periodic reporting.")
 
           (qof-query-set-book query (gnc-get-current-book))
           (xaccQueryAddAccountMatch query c_account_1 QOF-GUID-MATCH-ANY QOF-QUERY-AND)
-          (xaccQueryAddDateMatchTT query #t begindate #t enddate QOF-QUERY-AND)
+          (if (not split->date)
+              (xaccQueryAddDateMatchTT query #t begindate #t enddate QOF-QUERY-AND))
           (case void-status
             ((non-void-only) (gnc:query-set-match-non-voids-only! query (gnc-get-current-book)))
             ((void-only)     (gnc:query-set-match-voids-only! query (gnc-get-current-book)))
@@ -1941,6 +1984,7 @@ be excluded from periodic reporting.")
                 (set! splits (stable-sort! splits primary-comparator?))))
 
           ;; Combined Filter:
+          ;; - include/exclude using split->date according to date options
           ;; - include/exclude splits to/from selected accounts
           ;; - substring/regex matcher for Transaction Description/Notes/Memo
           ;; - custom-split-filter, a split->bool function for derived reports
@@ -1951,7 +1995,12 @@ be excluded from periodic reporting.")
                                            (if transaction-matcher-regexp
                                                (regexp-exec transaction-matcher-regexp str)
                                                (string-contains str transaction-matcher)))))
-                            (and (case filter-mode
+                            (and (or (not split->date)                  ; #f = ignore custom date filter
+                                     (let ((date (split->date split)))  ; cache split->date time64 or #f.
+                                       (if date                         ; if a split->date exists,
+                                           (<= begindate date enddate)  ; then check for inclusion;
+                                           split->date-include-false?))); else behave according to parameter
+                                 (case filter-mode
                                    ((none) #t)
                                    ((include) (is-filter-member split c_account_2))
                                    ((exclude) (not (is-filter-member split c_account_2))))
@@ -2029,7 +2078,13 @@ be excluded from periodic reporting.")
  'name (_ "Reconciliation Report")
  'report-guid "e45218c6d76f11e7b5ef0800277ef320"
  'options-generator reconcile-report-options-generator
- 'renderer trep-renderer)
+ ;; the renderer is the same as trep, however we're using a different split-date strategy.
+ ;; we're comparing reconcile date for inclusion, and if split is unreconciled, include it anyway.
+ 'renderer (lambda (rpt) (trep-renderer rpt
+                                        #:custom-calculated-cells reconcile-report-calculated-cells
+                                        #:split->date split->reconcile-date
+                                        #:split->date-include-false? #t
+                                        #:empty-report-message reconcile-report-instructions)))
 
 ;; Define the report.
 (gnc:define-report

commit 92ea3ba8a60bf4eb19d9b6932fb3ed8b582551a5
Author: Christian Stimming <christian at cstimming.de>
Date:   Fri Jun 22 22:17:38 2018 +0200

    Register speed-up for large files.
    
    The function qof_book_use_split_action_for_num_field gets called quite a
    lot in each register display refresh (due to sorting all splits from
    Split.x's xaccSplitOrder function), but it always used to use a KVP
    lookup, which is rather expensive compared to accessing a gboolean member
    variable.
    
    To get rid of this cost, I had to remove the KVP lookup in this
    simple-looking function. The pattern is this: A gboolean cache variable is
    introduced, along with an isvalid flag. The lookup makes the expensive
    KVP lookup once, then caches the value. The GObject property mechanism
    offers a callback for when the setter was called, which is used to mark
    the cached value as invalid. A parallel setter method (here:
    qof_book_set_option) also just marks the cache as invalid. This covers
    all setters, and the getters will use the cached value except for their
    first invocation.
    
    The NUM_FIELD_SOURCE feature was introduced in 2012 by the very large
    commit 7cdd7372 and apparently its costs never were a problem
    until the KVP lookup became more costly due to the std::vector
    construction and destruction.

diff --git a/libgnucash/engine/qofbook.cpp b/libgnucash/engine/qofbook.cpp
index 9c3974c..bf54dcc 100644
--- a/libgnucash/engine/qofbook.cpp
+++ b/libgnucash/engine/qofbook.cpp
@@ -92,6 +92,13 @@ enum
     N_PROPERTIES		/* Just a counter */
 };
 
+static void
+qof_book_option_num_field_source_changed_cb (GObject *gobject,
+                                             GParamSpec *pspec,
+                                             gpointer    user_data);
+// Use a #define for the GParam name to avoid typos
+#define PARAM_NAME_NUM_FIELD_SOURCE "split-action-num-field"
+
 QOF_GOBJECT_GET_TYPE(QofBook, qof_book, QOF_TYPE_INSTANCE, {});
 QOF_GOBJECT_DISPOSE(qof_book);
 QOF_GOBJECT_FINALIZE(qof_book);
@@ -126,6 +133,15 @@ qof_book_init (QofBook *book)
     book->read_only = FALSE;
     book->session_dirty = FALSE;
     book->version = 0;
+    book->cached_num_field_source_isvalid = FALSE;
+
+    // Register a callback on this NUM_FIELD_SOURCE property of that object
+    // because it gets called quite a lot, so that its value must be stored in
+    // a bool member variable instead of a KVP lookup on each getter call.
+    g_signal_connect (G_OBJECT(book),
+                      "notify::" PARAM_NAME_NUM_FIELD_SOURCE,
+                      G_CALLBACK (qof_book_option_num_field_source_changed_cb),
+                      book);
 }
 
 static const std::string str_KVP_OPTION_PATH(KVP_OPTION_PATH);
@@ -301,7 +317,7 @@ qof_book_class_init (QofBookClass *klass)
     g_object_class_install_property
     (gobject_class,
      PROP_OPT_NUM_FIELD_SOURCE,
-     g_param_spec_string("split-action-num-field",
+     g_param_spec_string(PARAM_NAME_NUM_FIELD_SOURCE,
                          "Use Split-Action in the Num Field",
 			 "Scheme true ('t') or NULL. If 't', then the book "
 			 "will put the split action value in the Num field.",
@@ -1002,14 +1018,42 @@ qof_book_use_trading_accounts (const QofBook *book)
 gboolean
 qof_book_use_split_action_for_num_field (const QofBook *book)
 {
-    const char *opt = NULL;
-    qof_instance_get (QOF_INSTANCE (book),
-		      "split-action-num-field", &opt,
-		      NULL);
+    g_assert(book);
+    if (!book->cached_num_field_source_isvalid)
+    {
+        // No cached value? Then do the expensive KVP lookup
+        gboolean result;
+        const char *opt = NULL;
+        qof_instance_get (QOF_INSTANCE (book),
+                          PARAM_NAME_NUM_FIELD_SOURCE, &opt,
+                          NULL);
+
+        if (opt && opt[0] == 't' && opt[1] == 0)
+            result = TRUE;
+        else
+            result = FALSE;
+
+        // We need to const_cast the "book" argument into a non-const pointer,
+        // but as we are dealing only with cache variables, I think this is
+        // understandable enough.
+        const_cast<QofBook*>(book)->cached_num_field_source = result;
+        const_cast<QofBook*>(book)->cached_num_field_source_isvalid = TRUE;
+    }
+    // Value is cached now. Use the cheap variable returning.
+    return book->cached_num_field_source;
+}
 
-    if (opt && opt[0] == 't' && opt[1] == 0)
-        return TRUE;
-    return FALSE;
+// The callback that is called when the KVP option value of
+// "split-action-num-field" changes, so that we mark the cached value as
+// invalid.
+static void
+qof_book_option_num_field_source_changed_cb (GObject *gobject,
+                                             GParamSpec *pspec,
+                                             gpointer    user_data)
+{
+    QofBook *book = reinterpret_cast<QofBook*>(user_data);
+    g_return_if_fail(QOF_IS_BOOK(book));
+    book->cached_num_field_source_isvalid = FALSE;
 }
 
 gboolean qof_book_uses_autoreadonly (const QofBook *book)
@@ -1188,6 +1232,9 @@ qof_book_set_option (QofBook *book, KvpValue *value, GSList *path)
     delete root->set_path(gslist_to_option_path(path), value);
     qof_instance_set_dirty (QOF_INSTANCE (book));
     qof_book_commit_edit (book);
+
+    // Also, mark any cached value as invalid
+    book->cached_num_field_source_isvalid = FALSE;
 }
 
 KvpValue*
diff --git a/libgnucash/engine/qofbook.h b/libgnucash/engine/qofbook.h
index 8cc1655..c695de3 100644
--- a/libgnucash/engine/qofbook.h
+++ b/libgnucash/engine/qofbook.h
@@ -147,6 +147,13 @@ struct _QofBook
      * except that it provides a nice convenience, avoiding a lookup
      * from the session.  Better solutions welcome ... */
     QofBackend *backend;
+
+    /* A cached value of the OPTION_NAME_NUM_FIELD_SOURCE option value because
+     * it is queried quite a lot, so we want to avoid a KVP lookup on each query
+     */
+    gboolean cached_num_field_source;
+    /* Whether the above cached value is valid. */
+    gboolean cached_num_field_source_isvalid;
 };
 
 struct _QofBookClass

commit f144a8deb7cd4f4595719d1902c857953e6978ff
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Jun 22 10:14:21 2018 -0700

    Revert "Bug 796248 - Editing Scheduled Transaction"
    
    This reverts commit ffe6044cd66edf39ae22875ef475d8e655b93526.
    It breaks loading splits on transactions that have already
    been loaded.

diff --git a/libgnucash/backend/sql/gnc-transaction-sql.cpp b/libgnucash/backend/sql/gnc-transaction-sql.cpp
index c62f3f1..f2e2b1f 100644
--- a/libgnucash/backend/sql/gnc-transaction-sql.cpp
+++ b/libgnucash/backend/sql/gnc-transaction-sql.cpp
@@ -292,12 +292,12 @@ load_single_tx (GncSqlBackend* sql_be, GncSqlRow& row)
     if (guid == NULL) return NULL;
     tx_guid = *guid;
 
-    /* Don't overwrite the transaction if it's already been loaded (and possibly
-     * modified).
-     */
+    // Don't overwrite the transaction if it's already been loaded (and possibly modified).
+    // However increase the edit level, it may be modified while loading its splits
     pTx = xaccTransLookup (&tx_guid, sql_be->book());
     if (pTx != NULL)
     {
+        xaccTransBeginEdit (pTx);
         return NULL;
     }
 

commit 696277bbf69550f8446d6865085c3fe24d89b3c7
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Jun 22 09:09:13 2018 -0700

    New clang error enforces virtual destructors.

diff --git a/gnucash/import-export/csv-imp/gnc-imp-settings-csv.hpp b/gnucash/import-export/csv-imp/gnc-imp-settings-csv.hpp
index b7daed0..fa283bf 100644
--- a/gnucash/import-export/csv-imp/gnc-imp-settings-csv.hpp
+++ b/gnucash/import-export/csv-imp/gnc-imp-settings-csv.hpp
@@ -55,6 +55,7 @@ struct CsvImportSettings
             m_date_format {0}, m_currency_format {0},
             m_skip_start_lines{0}, m_skip_end_lines{0}, m_skip_alt_lines (false),
             m_separators {","}, m_load_error {false} { }
+    virtual ~CsvImportSettings() = default;
 
 /** Save the gathered widget properties to a key File.
  *
diff --git a/libgnucash/backend/sql/gnc-sql-column-table-entry.hpp b/libgnucash/backend/sql/gnc-sql-column-table-entry.hpp
index 9e3809b..ce4dfe2 100644
--- a/libgnucash/backend/sql/gnc-sql-column-table-entry.hpp
+++ b/libgnucash/backend/sql/gnc-sql-column-table-entry.hpp
@@ -140,6 +140,7 @@ public:
         m_flags{static_cast<ColumnFlags>(f)},
         m_gobj_param_name{gobj_name}, m_qof_param_name{qof_name}, m_getter{get},
         m_setter{set} {}
+    virtual ~GncSqlColumnTableEntry() = default;
 
     /**
      * Load a value into an object from the database row.
@@ -254,7 +255,7 @@ private:
 };
 
 template <GncSqlObjectType Type>
-class GncSqlColumnTableEntryImpl : public GncSqlColumnTableEntry
+class GncSqlColumnTableEntryImpl final : public GncSqlColumnTableEntry
 {
 public:
     GncSqlColumnTableEntryImpl (const char* name, const GncSqlObjectType type,
@@ -265,6 +266,7 @@ public:
                                 QofSetterFunc set = nullptr) :
         GncSqlColumnTableEntry (name, type, s, f, gobj_name,qof_name, get, set)
         {}
+
     void load(const GncSqlBackend* sql_be, GncSqlRow& row,  QofIdTypeConst obj_name,
               void* pObject) const noexcept override;
     void add_to_table(ColVec& vec) const noexcept override;
diff --git a/libgnucash/backend/sql/gnc-sql-object-backend.hpp b/libgnucash/backend/sql/gnc-sql-object-backend.hpp
index b6a007e..316e64d 100644
--- a/libgnucash/backend/sql/gnc-sql-object-backend.hpp
+++ b/libgnucash/backend/sql/gnc-sql-object-backend.hpp
@@ -54,6 +54,7 @@ public:
                          const std::string& table, const EntryVec& vec) :
         m_table_name{table}, m_version{version}, m_type_name{type},
         m_col_table(vec) {}
+    virtual ~GncSqlObjectBackend() = default;
     /**
      * Load all objects of m_type in the database into memory.
      * @param sql_be The GncSqlBackend containing the database connection.

commit 10e20f97c644997ba8697d5ff3c48ce4e48a5fb3
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Fri Jun 22 15:23:06 2018 +0800

    [test-TR] add tests for reconcile report, date filter

diff --git a/gnucash/report/standard-reports/test/test-transaction.scm b/gnucash/report/standard-reports/test/test-transaction.scm
index b60226f..1418987 100644
--- a/gnucash/report/standard-reports/test/test-transaction.scm
+++ b/gnucash/report/standard-reports/test/test-transaction.scm
@@ -39,6 +39,7 @@
 
 ;; copied from transaction.scm
 (define trep-uuid "2fe3b9833af044abb929a88d5a59620f")
+(define reconcile-uuid "e45218c6d76f11e7b5ef0800277ef320")
 
 ;; Explicitly set locale to make the report output predictable
 (setlocale LC_ALL "C")
@@ -64,6 +65,7 @@
   (test-begin "transaction.scm")
   (null-test)
   (trep-tests)
+  (reconcile-tests)
   ;; (test-end) must be run as the last function, it will
   ;; return #f if any of the tests have failed.
   (test-end "transaction.scm"))
@@ -860,3 +862,52 @@
           (get-row-col sxml #f 6))))
     (test-end "subtotal table")
     ))
+
+(define (reconcile-tests)
+  (let* ((env (create-test-env))
+         (account-alist (env-create-account-structure-alist env structure))
+         (bank (cdr (assoc "Bank" account-alist)))
+         (income (cdr (assoc "Income" account-alist)))
+         (liability (cdr (assoc "Liabilities" account-alist)))
+         (expense (cdr (assoc "Expenses" account-alist)))
+         (YEAR (gnc:time64-get-year (gnc:get-today)))
+         )
+
+    (define (options->sxml options test-title)
+      (gnc:options->sxml reconcile-uuid options "test-reconcile" test-title))
+
+    (define (default-testing-options)
+      (let ((options (gnc:make-report-options reconcile-uuid)))
+        (set-option! options "Accounts" "Accounts" (list bank liability))
+        options))
+
+    ;; old transactions for testing reconcile date options
+    (env-transfer env 01 01 1970 bank expense       5   #:description "desc-1" #:num "trn1" #:memo "memo-3")
+    (env-transfer env 31 12 1969 income bank       10   #:description "desc-2" #:num "trn2" #:void-reason "void" #:notes "notes3")
+    (env-transfer env 31 12 1969 income bank       29   #:description "desc-3" #:num "trn3"
+                  #:reconcile (cons #\c (gnc-dmy2time64 01 03 1970)))
+    (env-transfer env 01 02 1970 bank expense      15   #:description "desc-4" #:num "trn4" #:notes "notes2" #:memo "memo-1")
+    (env-transfer env 10 01 1970 liability expense 10   #:description "desc-5" #:num "trn5" #:void-reason "any")
+    (env-transfer env 10 01 1970 liability expense 11   #:description "desc-6" #:num "trn6" #:notes "notes1")
+    (env-transfer env 10 02 1970 bank expense       8   #:description "desc-7" #:num "trn7" #:notes "notes1" #:memo "memo-2"
+                  #:reconcile (cons #\y (gnc-dmy2time64 01 03 1970)))
+
+
+    (let* ((options (default-testing-options)))
+      (let ((sxml (options->sxml options "null test")))
+        (test-assert "sxml"
+          sxml))
+      (set-option! options "General" "Start Date" (cons 'absolute (gnc-dmy2time64 01 03 1970)))
+      (set-option! options "General" "End Date" (cons 'absolute (gnc-dmy2time64 31 03 1970)))
+      (let ((sxml (options->sxml options "filter reconcile date")))
+        (test-equal "test reconciled amounts = $8"
+          (list "Total For Reconciled" "-$8.00")
+          (get-row-col sxml 3 #f))
+        (test-equal "test cleared amounts = $29"
+          (list "Total For Cleared" "$29.00")
+          (get-row-col sxml 6 #f))
+        (test-equal "test unreconciled amounts = -$31"
+          (list "Total For Unreconciled" "-$31.00")
+          (get-row-col sxml 11 #f))
+        sxml)
+      )))

commit 32e799484313dc3390f97fca38f6906e7c683048
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Thu Jun 21 16:23:20 2018 +0200

    Restore mechanism to prevent windows from being restored off-screen on opening a book
    
    This got disabled in commit 54019608ee in the gtk3 refactoring by accident.
    While we now no longer save the negative coordinates for hidden windows,
    there are still other situations in which this could happen, like switching
    from a dual monitor to a single monitor setup (common with laptops).

diff --git a/gnucash/gnome-utils/gnc-main-window.c b/gnucash/gnome-utils/gnc-main-window.c
index 1b0b651..d28da6b 100644
--- a/gnucash/gnome-utils/gnc-main-window.c
+++ b/gnucash/gnome-utils/gnc-main-window.c
@@ -762,15 +762,15 @@ gnc_main_window_restore_window (GncMainWindow *window, GncMainWindowSaveData *da
         g_warning("invalid number of values for group %s key %s",
                   window_group, WINDOW_POSITION);
     }
-// This does not do any thing ?
-//    else if ((pos[0] + (geom ? geom[0] : 0) < 0) ||
-//             (pos[0] > gdk_screen_width()) ||
-//             (pos[1] + (geom ? geom[1] : 0) < 0) ||
-//             (pos[1] > gdk_screen_height()))
-//    {
-//    g_debug("position %dx%d, size%dx%d is offscreen; will not move",
-//	    pos[0], pos[1], geom[0], geom[1]);
-//    }
+    /* Prevent restoring coordinates if this would move the window off-screen */
+    else if ((pos[0] + (geom ? geom[0] : 0) < 0) ||
+             (pos[0] > gdk_screen_width()) ||
+             (pos[1] + (geom ? geom[1] : 0) < 0) ||
+             (pos[1] > gdk_screen_height()))
+    {
+        g_debug("position %dx%d, size%dx%d is offscreen; will not move",
+                pos[0], pos[1], geom[0], geom[1]);
+    }
     else
     {
         gtk_window_move(GTK_WINDOW(window), pos[0], pos[1]);

commit 7a33c6c0ba3742ba77d6838626dad8f7b69b6a23
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Thu Jun 21 12:07:19 2018 +0100

    Added some padding to the numbers in Budget view
    
    Added some padding to the numbers so they are separated from the next
    more clearly.

diff --git a/gnucash/gnome/gnc-budget-view.c b/gnucash/gnome/gnc-budget-view.c
index 964a522..4059775 100644
--- a/gnucash/gnome/gnc-budget-view.c
+++ b/gnucash/gnome/gnc-budget-view.c
@@ -1125,6 +1125,16 @@ gbv_refresh_col_titles(GncBudgetView *view)
     }
 }
 
+static void
+gbv_renderer_add_padding (GtkCellRenderer *renderer)
+{
+    gint xpad, ypad;
+
+    gtk_cell_renderer_get_padding (renderer, &xpad, &ypad);
+    if (xpad < 5)
+        gtk_cell_renderer_set_padding (renderer, 5, ypad);
+}
+
 /** \brief Function to create the totals column to the right of the view.
 */
 static GtkTreeViewColumn*
@@ -1140,6 +1150,9 @@ gbv_create_totals_column(GncBudgetView* view, gint period_num)
     renderer = gtk_cell_renderer_text_new();
     col = gtk_tree_view_column_new_with_attributes("", renderer, NULL);
 
+    // add some padding to the right of the numbers
+    gbv_renderer_add_padding (renderer);
+
     gtk_tree_view_column_set_cell_data_func(col, renderer, totals_col_source, view, NULL);
     g_object_set_data(G_OBJECT(col), "budget", priv->budget);
     g_object_set_data(G_OBJECT(col), "period_num", GUINT_TO_POINTER(period_num));
@@ -1233,6 +1246,9 @@ gnc_budget_view_refresh(GncBudgetView *view)
         // as we only have one renderer/column, use this function to get it
         renderer = gnc_tree_view_column_get_renderer (col);
 
+        // add some padding to the right of the numbers
+        gbv_renderer_add_padding (renderer);
+
         g_signal_connect(G_OBJECT(renderer), "edited", (GCallback)gbv_col_edited_cb, view);
 
         col = gbv_create_totals_column(view, num_periods_visible);
@@ -1252,6 +1268,7 @@ gnc_budget_view_refresh(GncBudgetView *view)
         gchar title[MAX_DATE_LENGTH];
         guint titlelen;
         GDate *date;
+        GtkCellRenderer* renderer;
 
         priv->total_col = gnc_tree_view_account_add_custom_column(
                               GNC_TREE_VIEW_ACCOUNT(priv->tree_view), _("Total"),
@@ -1276,6 +1293,12 @@ gnc_budget_view_refresh(GncBudgetView *view)
         g_date_free (date);
         g_object_set_data(G_OBJECT(priv->total_col), "budget", priv->budget);
 
+        // as we only have one renderer/column, use this function to get it
+        renderer = gnc_tree_view_column_get_renderer (priv->total_col);
+
+        // add some padding to the right of the numbers
+        gbv_renderer_add_padding (renderer);
+
         col = gbv_create_totals_column(view, -1);
         if (col != NULL)
         {

commit e1288360137e59f37b64841aeed1d54e0844f57b
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Thu Jun 21 11:43:35 2018 +0100

    Simplify getting the cell renderer in budget view

diff --git a/gnucash/gnome/gnc-budget-view.c b/gnucash/gnome/gnc-budget-view.c
index 7fe15fe..964a522 100644
--- a/gnucash/gnome/gnc-budget-view.c
+++ b/gnucash/gnome/gnc-budget-view.c
@@ -1219,8 +1219,7 @@ gnc_budget_view_refresh(GncBudgetView *view)
     /* Create any needed columns */
     while (num_periods_visible < num_periods)
     {
-        GList* renderer_list;
-        GList* renderer_node;
+        GtkCellRenderer* renderer;
 
         col = gnc_tree_view_account_add_custom_column(
                   GNC_TREE_VIEW_ACCOUNT(priv->tree_view), "",
@@ -1231,13 +1230,10 @@ gnc_budget_view_refresh(GncBudgetView *view)
                           GUINT_TO_POINTER(num_periods_visible));
         col_list = g_list_append(col_list, col);
 
-        renderer_list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(col));
-        for (renderer_node = renderer_list; renderer_node != NULL; renderer_node = g_list_next(renderer_node))
-        {
-            GtkCellRenderer* renderer = GTK_CELL_RENDERER(renderer_node->data);
-            g_signal_connect(G_OBJECT(renderer), "edited", (GCallback)gbv_col_edited_cb, view);
-        }
-        g_list_free(renderer_list);
+        // as we only have one renderer/column, use this function to get it
+        renderer = gnc_tree_view_column_get_renderer (col);
+
+        g_signal_connect(G_OBJECT(renderer), "edited", (GCallback)gbv_col_edited_cb, view);
 
         col = gbv_create_totals_column(view, num_periods_visible);
         if (col != NULL)

commit 3f9bb7a109133e6a41f0a2db4e3bdd6346da70ba
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Thu Jun 21 11:40:52 2018 +0100

    Bug 795471 - Make sure account column has expand option
    
    By default the account column is the expand column but if it is manually
     changed this is reset to the last column. If Gnucash is maximised in
    this state, the totals column then has a lot of white space so this
    change reapplies the expand setting to the account column so it has the
    extra space.

diff --git a/gnucash/gnome/gnc-budget-view.c b/gnucash/gnome/gnc-budget-view.c
index fd64636..7fe15fe 100644
--- a/gnucash/gnome/gnc-budget-view.c
+++ b/gnucash/gnome/gnc-budget-view.c
@@ -389,6 +389,9 @@ gbv_create_widget(GncBudgetView *view)
     selection = gtk_tree_view_get_selection(tree_view);
     gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
 
+    // make sure the account column is the expand column
+    gnc_tree_view_expand_columns (GNC_TREE_VIEW(tree_view), "name", NULL);
+
     // Accounts filter
     priv->fd->tree_view = GNC_TREE_VIEW_ACCOUNT(priv->tree_view);
     gnc_tree_view_account_set_filter(
@@ -696,6 +699,8 @@ gbv_treeview_resized_cb(GtkWidget* widget, GtkAllocation* allocation, GncBudgetV
             j++;
         }
     }
+    // make sure the account column is the expand column
+    gnc_tree_view_expand_columns (GNC_TREE_VIEW(priv->tree_view), "name", NULL);
     LEAVE("");
 }
 

commit 4485966becbce68a0f94eb25a4c054bd3528a2c4
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Thu Jun 21 11:36:45 2018 +0200

    Bug 796638 - configuration not properly saved for CSV transactions import form

diff --git a/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp
index 80a871c..b7369d7 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -1614,21 +1614,30 @@ void CsvImpTransAssist::preview_refresh_table ()
 void
 CsvImpTransAssist::preview_refresh ()
 {
+    // Cache skip settings. Updating the widgets one by one
+    // triggers a callback the transfers all skip widgets'
+    // values to settings. So by the time the next widget value
+    // is to be set, that widget's 'new' setting has already been replaced by
+    // its old setting preventing us from using it here sensibly.
+    // Another solution might have been to delay callbacks from running
+    // until after all values are set.
+    auto skip_start_lines = tx_imp->skip_start_lines();
+    auto skip_end_lines = tx_imp->skip_end_lines();
+    auto skip_alt_lines = tx_imp->skip_alt_lines();
+
     // Set start row
     auto adj = gtk_spin_button_get_adjustment (start_row_spin);
     gtk_adjustment_set_upper (adj, tx_imp->m_parsed_lines.size());
-    gtk_spin_button_set_value (start_row_spin,
-            tx_imp->skip_start_lines());
+    gtk_spin_button_set_value (start_row_spin, skip_start_lines);
 
     // Set end row
     adj = gtk_spin_button_get_adjustment (end_row_spin);
     gtk_adjustment_set_upper (adj, tx_imp->m_parsed_lines.size());
-    gtk_spin_button_set_value (end_row_spin,
-            tx_imp->skip_end_lines());
+    gtk_spin_button_set_value (end_row_spin, skip_end_lines);
 
     // Set Alternate rows
     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(skip_alt_rows_button),
-            tx_imp->skip_alt_lines());
+            skip_alt_lines);
 
     // Set multi-split indicator
     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(multi_split_cbutton),

commit a2983935a2ea1a6a20c2047fb807b3838364c882
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Thu Jun 21 10:31:27 2018 +0200

    Use a comment to indicate full account name in saved register state
    
    This replaces the 'account_name' key in the same state. Keys should only be used
    data that's actually parsed when reading the state. Using a key for account_name here
    can create the false impression this data can be modified. A comment makes it much more
    clear the name is only informational while keeping the convenience.

diff --git a/gnucash/register/register-gnome/table-gnome.c b/gnucash/register/register-gnome/table-gnome.c
index 3650721..c03abde 100644
--- a/gnucash/register/register-gnome/table-gnome.c
+++ b/gnucash/register/register-gnome/table-gnome.c
@@ -61,8 +61,6 @@
 
 #define UNUSED_VAR     __attribute__ ((unused))
 
-#define KEY_ACCOUNT_NAME        "account_name"
-
 /* This static indicates the debugging module that this .o belongs to. */
 static QofLogModule UNUSED_VAR log_module = GNC_MOD_REGISTER;
 
@@ -87,6 +85,10 @@ gnc_table_save_state (Table *table, gchar * state_section, gchar * account_fulln
     if (!gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_SAVE_GEOMETRY))
         return;
 
+    key = g_strdup_printf ("Register state for \"%s\"", account_fullname);
+    g_key_file_set_comment (state_file, state_section, NULL, key, NULL);
+    g_free (key);
+
     sheet = GNUCASH_SHEET (table->ui_data);
 
     widths = gnc_header_widths_new ();
@@ -111,7 +113,6 @@ gnc_table_save_state (Table *table, gchar * state_section, gchar * account_fulln
             g_key_file_remove_key (state_file, state_section, key, NULL);
         g_free (key);
     }
-    g_key_file_set_string (state_file, state_section, KEY_ACCOUNT_NAME, account_fullname);
     gnc_header_widths_destroy (widths);
 }
 

commit 91af85ad14bf202a0ce37f5fa38bd56cbce9b732
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Sat Jun 16 11:29:38 2018 +0100

    Add a couple of tooltips to the register
    
    One for the reconcile cell which displays the reconciled date when
    reconciled or the void reason if voided. Also for the association cell
    to display the uri.

diff --git a/gnucash/register/ledger-core/split-register-model.c b/gnucash/register/ledger-core/split-register-model.c
index 8782d3c..b579548 100644
--- a/gnucash/register/ledger-core/split-register-model.c
+++ b/gnucash/register/ledger-core/split-register-model.c
@@ -508,6 +508,72 @@ gnc_split_register_get_default_tooltip (VirtualLocation virt_loc,
     return g_strdup (tooltip);
 }
 
+static char *
+gnc_split_register_get_recn_tooltip (VirtualLocation virt_loc,
+                                     gpointer user_data)
+{
+    SplitRegister *reg = user_data;
+    Split *split;
+
+    split = gnc_split_register_get_split (reg, virt_loc.vcell_loc);
+    if (!split)
+        return NULL;
+
+    if (xaccSplitGetReconcile (split) == YREC)
+    {
+        Timespec     ts = {0,0};
+        const char *str_rec_date;
+        xaccSplitGetDateReconciledTS (split, &ts);
+        str_rec_date = gnc_print_date (ts);
+        return g_strdup_printf (_("Reconciled on %s"), str_rec_date);
+    }
+    else if (xaccSplitGetReconcile (split) == VREC)
+    {
+        Transaction *trans = xaccSplitGetParent (split);
+        return g_strdup (xaccTransGetVoidReason (trans));
+    }
+    else
+        return NULL;
+}
+
+static char *
+gnc_split_register_get_associate_tooltip (VirtualLocation virt_loc,
+                                          gpointer user_data)
+{
+    SplitRegister *reg = user_data;
+    Transaction *trans;
+    const char *uri;
+
+    trans = gnc_split_register_get_trans (reg, virt_loc.vcell_loc);
+    if (!trans)
+        return NULL;
+
+    // get the existing uri
+    uri = xaccTransGetAssociation (trans);
+
+    // Check for uri is empty or NULL
+    if (g_strcmp0 (uri, "") != 0 && g_strcmp0 (uri, NULL) != 0)
+    {
+        gboolean valid_path_head = FALSE;
+        gchar *path_head = gnc_prefs_get_string (GNC_PREFS_GROUP_GENERAL, "assoc-head");
+
+        if ((path_head != NULL) && (g_strcmp0 (path_head, "") != 0)) // not default entry
+            valid_path_head = TRUE;
+
+        if (valid_path_head && g_str_has_prefix (uri,"file:/") && !g_str_has_prefix (uri,"file://"))
+        {
+            const gchar *part = uri + strlen ("file:");
+            gchar *new_uri = g_strconcat (path_head, part, NULL);
+            g_free (path_head);
+            return g_strdup (new_uri);
+        }
+        else
+            return g_strdup (uri);
+    }
+    else
+        return NULL;
+}
+
 static gnc_numeric
 get_trans_total_amount (SplitRegister *reg, Transaction *trans)
 {
@@ -2564,6 +2630,13 @@ gnc_split_register_model_new (void)
 //    gnc_table_model_set_default_tooltip_handler(
 //        model, gnc_split_register_get_default_tooltip);
 
+    gnc_table_model_set_tooltip_handler (model,
+                                       gnc_split_register_get_recn_tooltip,
+                                       RECN_CELL);
+
+    gnc_table_model_set_tooltip_handler (model,
+                                       gnc_split_register_get_associate_tooltip,
+                                       ASSOC_CELL);
 
 
     // help handlers

commit d906ac413618cce49bc2e0a451c5040ffad77bc3
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Jun 17 09:24:32 2018 -0700

    Prevent "quit without saving" dialog at shutdown when there's no session.
    
    Don't create a session if there isn't one.

diff --git a/gnucash/gnome-utils/gnc-file.c b/gnucash/gnome-utils/gnc-file.c
index aa88b28..2b2af4a 100644
--- a/gnucash/gnome-utils/gnc-file.c
+++ b/gnucash/gnome-utils/gnc-file.c
@@ -1615,6 +1615,8 @@ gnc_file_quit (void)
 {
     QofSession *session;
 
+    if (!gnc_current_session_exist ())
+        return;
     gnc_set_busy_cursor (NULL, TRUE);
     session = gnc_get_current_session ();
 
@@ -1640,6 +1642,10 @@ gnc_file_set_shutdown_callback (GNCShutdownCB cb)
 gboolean
 gnc_file_save_in_progress (void)
 {
-    QofSession *session = gnc_get_current_session();
-    return (qof_session_save_in_progress(session) || save_in_progress > 0);
+    if (gnc_current_session_exist())
+    {
+        QofSession *session = gnc_get_current_session();
+        return (qof_session_save_in_progress(session) || save_in_progress > 0);
+    }
+    return FALSE;
 }
diff --git a/gnucash/gnome-utils/gnc-main-window.c b/gnucash/gnome-utils/gnc-main-window.c
index 592517f..1b0b651 100644
--- a/gnucash/gnome-utils/gnc-main-window.c
+++ b/gnucash/gnome-utils/gnc-main-window.c
@@ -1232,7 +1232,8 @@ gnc_main_window_prompt_for_save (GtkWidget *window)
         _("If you don't save, changes from the past %d days and %d hours will be discarded.");
     time64 oldest_change;
     gint minutes, hours, days;
-
+    if (!gnc_current_session_exist())
+        return FALSE;
     session = gnc_get_current_session();
     book = qof_session_get_book(session);
     filename = qof_session_get_url(session);
@@ -1363,14 +1364,17 @@ static gboolean
 gnc_main_window_quit(GncMainWindow *window)
 {
     QofSession *session;
-    gboolean needs_save, do_shutdown;
-
-    session = gnc_get_current_session();
-    needs_save = qof_book_session_not_saved(qof_session_get_book(session)) &&
-                 !gnc_file_save_in_progress();
-    do_shutdown = !needs_save ||
-                  (needs_save && !gnc_main_window_prompt_for_save(GTK_WIDGET(window)));
-
+    gboolean needs_save, do_shutdown = TRUE;
+    if (gnc_current_session_exist())
+    {
+        session = gnc_get_current_session();
+        needs_save =
+            qof_book_session_not_saved(qof_session_get_book(session)) &&
+            !gnc_file_save_in_progress();
+        do_shutdown = !needs_save ||
+            (needs_save &&
+             !gnc_main_window_prompt_for_save(GTK_WIDGET(window)));
+    }
     if (do_shutdown)
     {
         g_timeout_add(250, gnc_main_window_timed_quit, NULL);
@@ -3758,10 +3762,12 @@ gnc_quartz_should_quit (GtkosxApplication *theApp, GncMainWindow *window)
     QofSession *session;
     gboolean needs_save;
 
-    if (!gnc_main_window_all_finish_pending() ||
-            gnc_file_save_in_progress())
+    if (!gnc_current_session_exist() ||
+        !gnc_main_window_all_finish_pending() ||
+        gnc_file_save_in_progress())
+
     {
-        return TRUE;
+        return FALSE;
     }
     session = gnc_get_current_session();
     needs_save = qof_book_session_not_saved(qof_session_get_book(session)) &&

commit a9f0fe2f93d490ec9470edc0d7a9d22b619cd25a
Merge: 1e3a445 186ac71
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Jun 17 14:06:38 2018 -0700

    Merge Chris Lam's 'maint-test-all-charts' into maint.


commit 1e3a4450072dc5c2f2fec75b6c506e4fd62bdc43
Author: Christian Stimming <christian at cstimming.de>
Date:   Sun Jun 17 22:23:16 2018 +0200

    Optimize calls to get_path_kvp so that std::strings are not always created and deleted immediately.
    
    Turns out that the on-the-fly conversion from const char* (the KVP_OPTION_PATH
    constants) to std::string with their immediate deletion afterwards is
    a quite costly operation. Avoiding this is surprisingly easy: Just keep
    local std::string objects at hand, and they don't have to be created
    and deleted anymore.
    
    The more optimized solution might be to turn the std::vector<std::string>
    into a std::vector<GQuark>, but this commit at least improves the picture for now.

diff --git a/libgnucash/engine/qofbook.cpp b/libgnucash/engine/qofbook.cpp
index e711749..9c3974c 100644
--- a/libgnucash/engine/qofbook.cpp
+++ b/libgnucash/engine/qofbook.cpp
@@ -128,6 +128,12 @@ qof_book_init (QofBook *book)
     book->version = 0;
 }
 
+static const std::string str_KVP_OPTION_PATH(KVP_OPTION_PATH);
+static const std::string str_OPTION_SECTION_ACCOUNTS(OPTION_SECTION_ACCOUNTS);
+static const std::string str_OPTION_NAME_TRADING_ACCOUNTS(OPTION_NAME_TRADING_ACCOUNTS);
+static const std::string str_OPTION_NAME_AUTO_READONLY_DAYS(OPTION_NAME_AUTO_READONLY_DAYS);
+static const std::string str_OPTION_NAME_NUM_FIELD_SOURCE(OPTION_NAME_NUM_FIELD_SOURCE);
+
 static void
 qof_book_get_property (GObject* object,
 		       guint prop_id,
@@ -142,32 +148,32 @@ qof_book_get_property (GObject* object,
     switch (prop_id)
     {
     case PROP_OPT_TRADING_ACCOUNTS:
-        qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {KVP_OPTION_PATH,
-                OPTION_SECTION_ACCOUNTS, OPTION_NAME_TRADING_ACCOUNTS});
+        qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
+                str_OPTION_SECTION_ACCOUNTS, str_OPTION_NAME_TRADING_ACCOUNTS});
         break;
     case PROP_OPT_BOOK_CURRENCY:
-        qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {KVP_OPTION_PATH,
-                OPTION_SECTION_ACCOUNTS, OPTION_NAME_BOOK_CURRENCY});
+        qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
+                str_OPTION_SECTION_ACCOUNTS, OPTION_NAME_BOOK_CURRENCY});
         break;
     case PROP_OPT_DEFAULT_GAINS_POLICY:
-        qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {KVP_OPTION_PATH,
-                OPTION_SECTION_ACCOUNTS, OPTION_NAME_DEFAULT_GAINS_POLICY});
+        qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
+                str_OPTION_SECTION_ACCOUNTS, OPTION_NAME_DEFAULT_GAINS_POLICY});
         break;
     case PROP_OPT_DEFAULT_GAINS_ACCOUNT_GUID:
-        qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {KVP_OPTION_PATH,
-                OPTION_SECTION_ACCOUNTS, OPTION_NAME_DEFAULT_GAINS_LOSS_ACCT_GUID});
+        qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
+                str_OPTION_SECTION_ACCOUNTS, OPTION_NAME_DEFAULT_GAINS_LOSS_ACCT_GUID});
         break;
     case PROP_OPT_AUTO_READONLY_DAYS:
-        qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {KVP_OPTION_PATH,
-                OPTION_SECTION_ACCOUNTS, OPTION_NAME_AUTO_READONLY_DAYS});
+        qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
+                str_OPTION_SECTION_ACCOUNTS, str_OPTION_NAME_AUTO_READONLY_DAYS});
         break;
     case PROP_OPT_NUM_FIELD_SOURCE:
-        qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {KVP_OPTION_PATH,
-                OPTION_SECTION_ACCOUNTS, OPTION_NAME_NUM_FIELD_SOURCE});
+        qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
+                str_OPTION_SECTION_ACCOUNTS, str_OPTION_NAME_NUM_FIELD_SOURCE});
         break;
     case PROP_OPT_DEFAULT_BUDGET:
-        qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {KVP_OPTION_PATH,
-                OPTION_SECTION_ACCOUNTS, OPTION_NAME_DEFAULT_BUDGET});
+        qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
+                str_OPTION_SECTION_ACCOUNTS, OPTION_NAME_DEFAULT_BUDGET});
         break;
     case PROP_OPT_FY_END:
         qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {"fy_end"});
@@ -198,32 +204,32 @@ qof_book_set_property (GObject      *object,
     switch (prop_id)
     {
     case PROP_OPT_TRADING_ACCOUNTS:
-        qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {KVP_OPTION_PATH,
-                OPTION_SECTION_ACCOUNTS, OPTION_NAME_TRADING_ACCOUNTS});
+        qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
+                str_OPTION_SECTION_ACCOUNTS, str_OPTION_NAME_TRADING_ACCOUNTS});
         break;
     case PROP_OPT_BOOK_CURRENCY:
-        qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {KVP_OPTION_PATH,
-                OPTION_SECTION_ACCOUNTS, OPTION_NAME_BOOK_CURRENCY});
+        qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
+                str_OPTION_SECTION_ACCOUNTS, OPTION_NAME_BOOK_CURRENCY});
         break;
     case PROP_OPT_DEFAULT_GAINS_POLICY:
-        qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {KVP_OPTION_PATH,
-                OPTION_SECTION_ACCOUNTS, OPTION_NAME_DEFAULT_GAINS_POLICY});
+        qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
+                str_OPTION_SECTION_ACCOUNTS, OPTION_NAME_DEFAULT_GAINS_POLICY});
         break;
     case PROP_OPT_DEFAULT_GAINS_ACCOUNT_GUID:
-        qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {KVP_OPTION_PATH,
-                OPTION_SECTION_ACCOUNTS, OPTION_NAME_DEFAULT_GAINS_LOSS_ACCT_GUID});
+        qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
+                str_OPTION_SECTION_ACCOUNTS, OPTION_NAME_DEFAULT_GAINS_LOSS_ACCT_GUID});
         break;
     case PROP_OPT_AUTO_READONLY_DAYS:
-        qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {KVP_OPTION_PATH,
-                OPTION_SECTION_ACCOUNTS, OPTION_NAME_AUTO_READONLY_DAYS});
+        qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
+                str_OPTION_SECTION_ACCOUNTS, str_OPTION_NAME_AUTO_READONLY_DAYS});
         break;
     case PROP_OPT_NUM_FIELD_SOURCE:
-        qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {KVP_OPTION_PATH,
-                OPTION_SECTION_ACCOUNTS, OPTION_NAME_NUM_FIELD_SOURCE});
+        qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
+                str_OPTION_SECTION_ACCOUNTS, str_OPTION_NAME_NUM_FIELD_SOURCE});
         break;
     case PROP_OPT_DEFAULT_BUDGET:
-        qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {KVP_OPTION_PATH,
-                OPTION_SECTION_ACCOUNTS, OPTION_NAME_DEFAULT_BUDGET});
+        qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
+                str_OPTION_SECTION_ACCOUNTS, OPTION_NAME_DEFAULT_BUDGET});
         break;
     case PROP_OPT_FY_END:
         qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {"fy_end"});
@@ -1164,7 +1170,7 @@ static Path gslist_to_option_path (GSList *gspath)
     Path tmp_path;
     if (!gspath) return tmp_path;
 
-    Path path_v {KVP_OPTION_PATH};
+    Path path_v {str_KVP_OPTION_PATH};
     for (auto item = gspath; item != nullptr; item = g_slist_next(item))
         tmp_path.push_back(static_cast<const char*>(item->data));
     if (tmp_path.front() == "counters")
@@ -1197,14 +1203,14 @@ qof_book_options_delete (QofBook *book, GSList *path)
     KvpFrame *root = qof_instance_get_slots(QOF_INSTANCE (book));
     if (path != nullptr)
     {
-        Path path_v {KVP_OPTION_PATH};
+        Path path_v {str_KVP_OPTION_PATH};
         Path tmp_path;
         for (auto item = path; item != nullptr; item = g_slist_next(item))
             tmp_path.push_back(static_cast<const char*>(item->data));
         delete root->set_path(gslist_to_option_path(path), nullptr);
     }
     else
-        delete root->set_path({KVP_OPTION_PATH}, nullptr);
+        delete root->set_path({str_KVP_OPTION_PATH}, nullptr);
 }
 
 /* QofObject function implementation and registration */

commit 4aaa18eafc82c4a7066a3c1299c4a8a9bbef7b99
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Jun 16 16:19:12 2018 -0700

    Bug 796600 - stock split cash-in-lieu income/asset labels backwards.

diff --git a/gnucash/gtkbuilder/assistant-stock-split.glade b/gnucash/gtkbuilder/assistant-stock-split.glade
index 1a9f847..b2a395b 100644
--- a/gnucash/gtkbuilder/assistant-stock-split.glade
+++ b/gnucash/gtkbuilder/assistant-stock-split.glade
@@ -336,7 +336,7 @@
                 <property name="use_underline">True</property>
               </object>
               <packing>
-                <property name="left_attach">0</property>
+                <property name="left_attach">1</property>
                 <property name="top_attach">0</property>
               </packing>
             </child>
@@ -349,7 +349,7 @@
                 <property name="use_underline">True</property>
               </object>
               <packing>
-                <property name="left_attach">1</property>
+                <property name="left_attach">0</property>
                 <property name="top_attach">0</property>
               </packing>
             </child>

commit 9b7544a77794184c8df4ec7f0a2c998471f44f95
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Jun 16 14:01:20 2018 -0700

    Fix build of windows resources file.

diff --git a/gnucash/CMakeLists.txt b/gnucash/CMakeLists.txt
index 84f764c..2ae906a 100644
--- a/gnucash/CMakeLists.txt
+++ b/gnucash/CMakeLists.txt
@@ -36,7 +36,7 @@ add_definitions (-DHAVE_CONFIG_H)
 # Some settings are platform dependent. Let's define them per platform.
 if (WIN32)
     # Windows specific settings go here:
-    set (GNUCASH_RESOURCE_FILE gnucash.rc)
+    set (GNUCASH_RESOURCE_FILE ${CMAKE_CURRENT_BINARY_DIR}/gnucash.rc)
     configure_file(gnucash.rc.in gnucash.rc @ONLY NEWLINE_STYLE WIN32)
 
 else (WIN32)
diff --git a/gnucash/gnucash.rc.in b/gnucash/gnucash.rc.in
index b2e2776..c488672 100644
--- a/gnucash/gnucash.rc.in
+++ b/gnucash/gnucash.rc.in
@@ -2,9 +2,9 @@
 
 APPLICATION_ICON ICON DISCARDABLE "../data/pixmaps/gnucash-icon.ico"
 
-VS_VERSION VERSIONINFO
- FILEVERSION     @GNUCASH_MAJOR_VERSION@, at GNUCASH_MINOR_VERSION@, at GNUCASH_MICRO_VERSION@,0
- PRODUCTVERSION  @GNUCASH_MAJOR_VERSION@, at GNUCASH_MINOR_VERSION@, at GNUCASH_MICRO_VERSION@,0
+1 VERSIONINFO
+ FILEVERSION     @GNUCASH_MAJOR_VERSION@, at GNUCASH_MINOR_VERSION@,0,0
+ PRODUCTVERSION  @GNUCASH_MAJOR_VERSION@, at GNUCASH_MINOR_VERSION@,0,0
  FILEFLAGSMASK   0x3fL
  FILEFLAGS       0x0L
  FILEOS          VOS__WINDOWS32
@@ -19,7 +19,7 @@ BEGIN
       VALUE "FileDescription", "GnuCash Program File"
       VALUE "FileVersion", "@VERSION@"
       VALUE "InternalName", "gnucash"
-      VALUE "LegalCopyright", "©2017 GnuCash Development Team, Licence: GPL v2.0 or later"
+      VALUE "LegalCopyright", "©2018 GnuCash Development Team, Licence: GPL v2.0 or later"
       VALUE "OriginalFilename", "gnucash.exe"
       VALUE "ProductName", "GnuCash Free Finance Manager"
       VALUE "ProductVersion", "@VERSION@"

commit c1103a3a52d8199664837e909aaa85801f136e6d
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Jun 16 10:37:58 2018 -0700

    Bug 796586 - QIF import incorrectly converts unicode characters from...
    
    UTF8 encoded file
    
    Force UTF-8 encoding; the line processing includes checking each
    line for legal UTF-8 and transcoding it to the locale encoding.

diff --git a/gnucash/import-export/qif-imp/qif-file.scm b/gnucash/import-export/qif-imp/qif-file.scm
index 2a0eb51..f053daa 100644
--- a/gnucash/import-export/qif-imp/qif-file.scm
+++ b/gnucash/import-export/qif-imp/qif-file.scm
@@ -516,7 +516,7 @@
                 ;; ...and this is if we read a null or eof line.
                 (if (and (not abort-read)
                          (not (eof-object? line)))
-                    (line-loop))))))
+                    (line-loop))))) #:encoding "UTF-8")
 
       ;; Reverse the transaction list so xtns are in the same order that
       ;; they appeared in the file.  This is important in a few cases.

commit 186ac71fcfbed3cca4e94c7bde39a41183861cfc
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sat Jun 16 20:52:19 2018 +0800

    Merge old tests with their support files.
    
    It bothered me that these old tests have 2 scheme files per test. It
    was always confusing.

diff --git a/gnucash/report/standard-reports/test/CMakeLists.txt b/gnucash/report/standard-reports/test/CMakeLists.txt
index 3cb8776..0eed778 100644
--- a/gnucash/report/standard-reports/test/CMakeLists.txt
+++ b/gnucash/report/standard-reports/test/CMakeLists.txt
@@ -13,12 +13,6 @@ set(scm_test_with_srfi64_SOURCES
   test-income-gst.scm
 )
 
-set(scm_test_report_SUPPORT
-  test-generic-category-report.scm
-  test-generic-net-barchart.scm
-  test-generic-net-linechart.scm
-  )
-
 set(GUILE_DEPENDS
   scm-gnc-module
   scm-app-utils
@@ -37,16 +31,10 @@ endif (HAVE_SRFI64)
 
 gnc_add_scheme_tests("${scm_test_standard_reports_SOURCES}")
 
-gnc_add_scheme_targets(scm-test-standard-support
-  "${scm_test_report_SUPPORT}"
-  "gnucash/report/standard-reports/test"
-  "${GUILE_DEPENDS}"
-  FALSE
-  )
 gnc_add_scheme_targets(scm-test-standard-reports
   "${scm_test_standard_reports_SOURCES}"
   gnucash/report/standard-reports/test
-  "scm-test-standard-support"
+  "${GUILE_DEPENDS}"
   FALSE
 )
 
@@ -54,4 +42,4 @@ add_dependencies(check scm-test-standard-reports)
 
 set_dist_list(test_standard_reports_DIST CMakeLists.txt
   ${scm_test_with_srfi64_SOURCES}
-  ${scm_test_standard_reports_SOURCES} ${scm_test_report_SUPPORT})
+  ${scm_test_standard_reports_SOURCES})
diff --git a/gnucash/report/standard-reports/test/test-generic-category-report.scm b/gnucash/report/standard-reports/test/test-generic-category-report.scm
deleted file mode 100644
index 4848b26..0000000
--- a/gnucash/report/standard-reports/test/test-generic-category-report.scm
+++ /dev/null
@@ -1,292 +0,0 @@
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; This program is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU General Public License as
-;; published by the Free Software Foundation; either version 2 of
-;; the License, or (at your option) any later version.
-;;
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-;;
-;; You should have received a copy of the GNU General Public License
-;; along with this program; if not, contact:
-;;
-;; Free Software Foundation           Voice:  +1-617-542-5942
-;; 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
-;; Boston, MA  02110-1301,  USA       gnu at gnu.org
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(define-module (gnucash report standard-reports test test-generic-category-report))
-
-(use-modules (ice-9 format))
-(use-modules (ice-9 streams))
-(use-modules (srfi srfi-1))
-
-(use-modules (gnucash gnc-module))
-(gnc:module-load "gnucash/report/report-system" 0)
-
-(use-modules (gnucash utilities)) 
-(use-modules (gnucash report report-system))
-(use-modules (gnucash app-utils))
-(use-modules (gnucash engine))
-(use-modules (sw_engine))
-(use-modules (gnucash report stylesheets))
-
-(use-modules (gnucash report report-system collectors))
-(use-modules (gnucash engine test test-extras))
-(use-modules (gnucash report report-system test test-extras))
-
-(export run-category-income-expense-test)
-(export run-category-asset-liability-test)
-
-(define (set-option report page tag value)
-  ((gnc:option-setter (gnc:lookup-option (gnc:report-options report)
-					 page tag)) value))
-
-
-(define constructor (record-constructor <report>))
-
-;(set-option income-report gnc:pagename-general "Start Date" (cons 'relative 'start-prev-year))
-;(set-option income-report gnc:pagename-general "End Date" (cons 'relative 'end-this-month))
-;(set-option income-report gnc:pagename-general "Show table" #t)
-;(set-option income-report gnc:pagename-general "Price Source" 'pricedb-nearest)
-;(set-option income-report gnc:pagename-general "Report's currency"  (gnc-default-report-currency))
-
-(define (run-category-income-expense-test income-report-uuid expense-report-uuid)
-  (and  (null-test income-report-uuid)
-	(null-test expense-report-uuid)
-	(single-txn-test income-report-uuid)
-	(multi-acct-test expense-report-uuid)
-	#t))
-
-(define (run-category-asset-liability-test asset-report-uuid liability-report-uuid)
-  (and (null-test asset-report-uuid)
-       (null-test liability-report-uuid)
-       (asset-test asset-report-uuid)
-       (liability-test liability-report-uuid)
-       #t))
-
-;; No real test here, just confirm that no exceptions are thrown
-(define (null-test uuid)
-  (let* ((template (gnc:find-report-template uuid))
-	 (options (gnc:make-report-options uuid))
-	 (report (constructor uuid "bar" options
-				     #t #t #f #f ""))
-	 (renderer (gnc:report-template-renderer template)))
-
-    (let ((doc (renderer report)))
-      (gnc:html-document-set-style-sheet! doc
-					  (gnc:report-stylesheet report))
-      #t
-      )))
-
-
-(define (single-txn-test uuid)
-  (let* ((income-template (gnc:find-report-template uuid))
-	 (income-options (gnc:make-report-options uuid))
-	 (income-report (constructor uuid "bar" income-options
-				     #t #t #f #f ""))
-	 (income-renderer (gnc:report-template-renderer income-template)))
-    (let* ((env (create-test-env))
-	   (my-asset-account (env-create-root-account env ACCT-TYPE-ASSET
-						      (gnc-default-report-currency)))
-	   (my-expense-account (env-create-root-account env ACCT-TYPE-EXPENSE
-							(gnc-default-report-currency)))
-	   (my-income-account (env-create-root-account env ACCT-TYPE-INCOME
-						       (gnc-default-report-currency))))
-      (env-create-daily-transactions env
-				     (gnc:get-start-this-month)
-				     (gnc:get-end-this-month)
-				     my-asset-account my-income-account)
-      (begin
-	(set-option income-report gnc:pagename-display "Show table" #t)
-	(set-option income-report gnc:pagename-general "Start Date" (cons 'relative 'start-this-month))
-	(set-option income-report gnc:pagename-general "End Date" (cons 'relative 'end-this-month))
-	(set-option income-report gnc:pagename-general "Step Size" 'DayDelta)
-	(set-option income-report gnc:pagename-general "Price Source" 'pricedb-nearest)
-	(set-option income-report gnc:pagename-general "Report's currency"  (gnc-default-report-currency))
-	(set-option income-report gnc:pagename-accounts "Accounts" (list my-income-account))
-	(set-option income-report gnc:pagename-accounts "Show Accounts until level"  'all)
-
-	(let ((doc (income-renderer income-report)))
-	  (gnc:html-document-set-style-sheet! doc
-					      (gnc:report-stylesheet income-report))
-	  (let* ((result (gnc:html-document-render doc #f))
-		 (tbl (stream->list
-		       (pattern-streamer "<tr>"
-					 (list (list "<td>([0-9][0-9])/([0-9][0-9])/([0-9][0-9])</td>" 1 2 3)
-					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1))
-					 result))))
-	    (every (lambda (date value-list)
-		     (let ((day (second date))
-			   (value (first value-list)))
-		       (= (string->number day) (string->number value))))
-		   (map first tbl)
-		   (map second tbl))))))))
-
-(define (list-leaves list)
-  (if (not (pair? list))
-      (cons list '())
-      (fold (lambda (next acc)
-	      (append (list-leaves next)
-		      acc))
-	    '()
-	    list)))
-
-(define (multi-acct-test expense-report-uuid)
-  (let* ((expense-template (gnc:find-report-template expense-report-uuid))
-	 (expense-options (gnc:make-report-options expense-report-uuid))
-	 (expense-report (constructor expense-report-uuid "bar" expense-options
-				     #t #t #f #f ""))
-	 (expense-renderer (gnc:report-template-renderer expense-template)))
-    (let* ((env (create-test-env))
-	   (expense-accounts (env-expense-account-structure env))
-	   (asset-accounts (env-create-account-structure
-			    env
-			    (list "Assets"
-				  (list (cons 'type ACCT-TYPE-ASSET))
-				  (list "Bank"))))
-	   (leaf-expense-accounts (list-leaves expense-accounts))
-	   (bank-account (car (car (cdr asset-accounts)))))
-      (for-each (lambda (expense-account)
-		  (env-create-daily-transactions env
-						 (gnc:get-start-this-month)
-						 (gnc:get-end-this-month)
-						 expense-account
-						 bank-account))
-		leaf-expense-accounts)
-      (begin
-	(set-option expense-report gnc:pagename-display "Show table" #t)
-	(set-option expense-report gnc:pagename-general "Start Date" (cons 'relative 'start-this-month))
-	(set-option expense-report gnc:pagename-general "End Date" (cons 'relative 'end-this-month))
-	(set-option expense-report gnc:pagename-general "Step Size" 'DayDelta)
-	(set-option expense-report gnc:pagename-general "Price Source" 'pricedb-nearest)
-	(set-option expense-report gnc:pagename-general "Report's currency"  (gnc-default-report-currency))
-	(set-option expense-report gnc:pagename-accounts "Accounts" leaf-expense-accounts)
-	(set-option expense-report gnc:pagename-accounts "Show Accounts until level" 2)
-
-	(let ((doc (expense-renderer expense-report)))
-	  (gnc:html-document-set-style-sheet! doc
-					      (gnc:report-stylesheet expense-report))
-	  (let* ((html-document (gnc:html-document-render doc #f))
-		 (columns (columns-from-report-document html-document))
-		 (tbl (stream->list
-		       (pattern-streamer "<tr>"
-					 (list (list "<td>([0-9][0-9])/([0-9][0-9])/([0-9][0-9])</td>" 1 2 3)
-					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
-					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
-					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
-					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
-					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1))
-					 html-document))))
-	    ;(format #t "~a" html-document)
-	    (and (= 6 (length columns))
-		 (equal? "Date" (first columns))
-		 (equal? "Auto" (second columns))
-		 ;; maybe should try to check actual values
-		 )))))))
-
-(define (columns-from-report-document doc)
-  (let ((columns (stream->list (pattern-streamer "<th>"
-						 (list (list "<th>([^<]*)</" 1))
-						 doc))))
-    (map caar columns)))
-
-;;
-;;
-;;
-
-(define (asset-test uuid)
-    (let* ((asset-template (gnc:find-report-template uuid))
-	   (asset-options (gnc:make-report-options uuid))
-	   (asset-report (constructor uuid "bar" asset-options
-				      #t #t #f #f ""))
-	   (asset-renderer (gnc:report-template-renderer asset-template)))
-      (let* ((env (create-test-env))
-	     (my-asset-account (env-create-root-account env ACCT-TYPE-ASSET
-							(gnc-default-report-currency)))
-	     (my-expense-account (env-create-root-account env ACCT-TYPE-EXPENSE
-							  (gnc-default-report-currency)))
-	     (my-income-account (env-create-root-account env ACCT-TYPE-INCOME
-							 (gnc-default-report-currency))))
-      (env-create-daily-transactions env
-				     (gnc:get-start-this-month)
-				     (gnc:get-end-this-month)
-				     my-asset-account my-income-account)
-      (begin
-	(set-option asset-report gnc:pagename-display "Show table" #t)
-	(set-option asset-report gnc:pagename-general "Start Date" (cons 'relative 'start-this-month))
-	(set-option asset-report gnc:pagename-general "End Date" (cons 'relative 'end-this-month))
-	(set-option asset-report gnc:pagename-general "Step Size" 'DayDelta)
-	(set-option asset-report gnc:pagename-general "Price Source" 'pricedb-nearest)
-	(set-option asset-report gnc:pagename-general "Price Source" 'pricedb-nearest)
-	(set-option asset-report gnc:pagename-general "Report's currency"  (gnc-default-report-currency))
-	(set-option asset-report gnc:pagename-accounts "Accounts" (list my-asset-account))
-	(set-option asset-report gnc:pagename-accounts "Show Accounts until level"  'all)
-
-	(let ((doc (asset-renderer asset-report)))
-	  (gnc:html-document-set-style-sheet! doc
-					      (gnc:report-stylesheet asset-report))
-	  (let* ((html-document (gnc:html-document-render doc #f))
-		 (columns (columns-from-report-document html-document))
-		 (tbl (stream->list
-		       (pattern-streamer "<tr>"
-					 (list (list "<td>([0-9][0-9])/([0-9][0-9])/([0-9][0-9])</td>" 1 2 3)
-					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1))
-					 html-document)))
-		 (row-count (tbl-row-count tbl)))
-	    (and (member "account-1" columns)
-			 (= 2 (length columns))
-			 (= 1 (string->number (car (tbl-ref tbl 0 1))))
-			 (= (/ (* row-count (+ row-count 1)) 2)
-			    (string->number (car (tbl-ref tbl (- row-count 1) 1))))
-			 #t)))))))
-
-(define (liability-test uuid)
-  ;; this test is tailored for bug 793278
-  ;; except we can't use $10,000 because the string->number
-  ;; function cannot handle thousand separators. Use $100.
-  (let* ((liability-template (gnc:find-report-template uuid))
-         (liability-options (gnc:make-report-options uuid))
-         (liability-report (constructor uuid "bar" liability-options
-                                        #t #t #f #f ""))
-         (liability-renderer (gnc:report-template-renderer liability-template)))
-    (let* ((env (create-test-env))
-           (asset--acc (env-create-root-account env ACCT-TYPE-ASSET (gnc-default-report-currency)))
-           (liabil-acc (env-create-root-account env ACCT-TYPE-CREDIT (gnc-default-report-currency)))
-           (income-acc (env-create-root-account env ACCT-TYPE-INCOME (gnc-default-report-currency))))
-      (env-create-transaction env (gnc-dmy2time64 01 10 2016) asset--acc liabil-acc 100) ;loan
-      (env-create-transaction env (gnc-dmy2time64 01 01 2017) asset--acc income-acc 10)  ;salary#1
-      (env-create-transaction env (gnc-dmy2time64 02 01 2017) liabil-acc asset--acc 9)   ;repay#1
-      (env-create-transaction env (gnc-dmy2time64 01 02 2017) asset--acc income-acc 10)  ;salary#2
-      (env-create-transaction env (gnc-dmy2time64 02 02 2017) liabil-acc asset--acc 9)   ;repay#2
-      (env-create-transaction env (gnc-dmy2time64 01 03 2017) asset--acc income-acc 10)  ;salary#3
-      (env-create-transaction env (gnc-dmy2time64 02 03 2017) liabil-acc asset--acc 9)   ;repay#3
-      (env-create-transaction env (gnc-dmy2time64 01 04 2017) asset--acc income-acc 10)  ;salary#4
-      (env-create-transaction env (gnc-dmy2time64 02 04 2017) liabil-acc asset--acc 9)   ;repay#4
-      (env-create-transaction env (gnc-dmy2time64 01 05 2017) asset--acc income-acc 10)  ;salary#5
-      (env-create-transaction env (gnc-dmy2time64 02 05 2017) liabil-acc asset--acc 9)   ;repay#5
-      (begin
-        (set-option liability-report gnc:pagename-display "Show table" #t)
-        (set-option liability-report gnc:pagename-general "Start Date" (cons 'absolute (gnc-dmy2time64 01 01 2017)))
-        (set-option liability-report gnc:pagename-general "End Date" (cons 'absolute (gnc-dmy2time64 31 12 2018)))
-        (set-option liability-report gnc:pagename-general "Step Size" 'MonthDelta)
-        (set-option liability-report gnc:pagename-general "Price Source" 'pricedb-nearest)
-        (set-option liability-report gnc:pagename-general "Report's currency"  (gnc-default-report-currency))
-        (set-option liability-report gnc:pagename-accounts "Accounts" (list liabil-acc))
-        (set-option liability-report gnc:pagename-accounts "Show Accounts until level"  'all)
-        (let ((doc (liability-renderer liability-report)))
-          (gnc:html-document-set-style-sheet! doc (gnc:report-stylesheet liability-report))
-          (let* ((html-document (gnc:html-document-render doc #f))
-                 (columns (columns-from-report-document html-document))
-                 (tbl (stream->list
-                       (pattern-streamer "<tr>"
-                                         (list (list "<td>([0-9][0-9])/([0-9][0-9])/([0-9][0-9])</td>" 1 2 3)
-                                               (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1))
-                                         html-document)))
-                 (row-count (tbl-row-count tbl)))
-            (and (= 2 (length columns))
-                 (= 100 (string->number (car (tbl-ref tbl 0 1))))
-                 (= 55 (string->number (car (tbl-ref tbl (- row-count 1) 1))))
-                 #t)))))))
diff --git a/gnucash/report/standard-reports/test/test-generic-net-barchart.scm b/gnucash/report/standard-reports/test/test-generic-net-barchart.scm
deleted file mode 100644
index d8585bb..0000000
--- a/gnucash/report/standard-reports/test/test-generic-net-barchart.scm
+++ /dev/null
@@ -1,366 +0,0 @@
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; This program is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU General Public License as
-;; published by the Free Software Foundation; either version 2 of
-;; the License, or (at your option) any later version.
-;;
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-;;
-;; You should have received a copy of the GNU General Public License
-;; along with this program; if not, contact:
-;;
-;; Free Software Foundation           Voice:  +1-617-542-5942
-;; 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
-;; Boston, MA  02110-1301,  USA       gnu at gnu.org
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(define-module (gnucash report standard-reports test test-generic-net-barchart))
-
-(use-modules (ice-9 format))
-(use-modules (ice-9 streams))
-(use-modules (srfi srfi-1))
-
-(use-modules (gnucash gnc-module))
-(gnc:module-load "gnucash/report/report-system" 0)
-(use-modules (gnucash report stylesheets))
-
-(use-modules (gnucash engine test test-extras))
-(use-modules (gnucash report report-system test test-extras))
-
-(export run-net-asset-income-test)
-
-(define (set-option report page tag value)
-  ((gnc:option-setter (gnc:lookup-option (gnc:report-options report)
-					 page tag)) value))
-
-
-(define constructor (record-constructor <report>))
-
-(define (run-net-asset-income-test asset-report-uuid income-report-uuid)
-  (and (two-txn-test asset-report-uuid)
-       (two-txn-test-2 asset-report-uuid)
-       (two-txn-test-income income-report-uuid)
-
-       (null-test asset-report-uuid)
-       (null-test income-report-uuid)
-       (single-txn-test asset-report-uuid)
-       (closing-test income-report-uuid)
-       #t))
-
-;; Just prove that the report exists.
-(define (null-test uuid)
-  (let* ((template (gnc:find-report-template uuid))
-	 (options (gnc:make-report-options uuid))
-	 (report (constructor uuid "bar" options
-			      #t #t #f #f ""))
-	 (renderer (gnc:report-template-renderer template)))
-
-    (let ((doc (renderer report)))
-      (gnc:html-document-set-style-sheet! doc
-					  (gnc:report-stylesheet report))
-      ;;(format #t "render: ~a\n" (gnc:html-document-render doc #f))
-      #t
-      )))
-
-(define (single-txn-test uuid)
-  (let* ((template (gnc:find-report-template uuid))
-	 (options (gnc:make-report-options uuid))
-	 (report (constructor uuid "bar" options
-				     #t #t #f #f ""))
-	 (renderer (gnc:report-template-renderer template)))
-    (let* ((env (create-test-env))
-	   (my-asset-account (env-create-root-account env ACCT-TYPE-ASSET
-						      (gnc-default-report-currency)))
-	   (my-expense-account (env-create-root-account env ACCT-TYPE-EXPENSE
-							(gnc-default-report-currency)))
-	   (my-income-account (env-create-root-account env ACCT-TYPE-INCOME
-						       (gnc-default-report-currency))))
-      (env-create-transaction env
-			       (gnc:get-start-this-month)
-			       my-income-account
-			       my-asset-account
-			       -1/1)
-      (begin
-	(set-option report gnc:pagename-display "Show table" #t)
-	(set-option report gnc:pagename-general "Start Date"
-		    (cons 'absolute (gnc:get-start-this-month)))
-	(set-option report gnc:pagename-general "End Date"
-		    (cons 'absolute (gnc:get-start-this-month)))
-	(set-option report gnc:pagename-general "Step Size" 'DayDelta)
-	(set-option report gnc:pagename-general "Price Source" 'pricedb-nearest)
-	(set-option report gnc:pagename-general "Report's currency"  (gnc-default-report-currency))
-	(set-option report gnc:pagename-accounts "Accounts" (list my-asset-account))
-
-	(let ((doc (renderer report)))
-	  (gnc:html-document-set-style-sheet! doc
-					      (gnc:report-stylesheet report))
-	  (let* ((result (gnc:html-document-render doc #f))
-		 (tbl (stream->list
-		       (pattern-streamer "<tr>"
-					 (list (list "<td>([0-9][0-9])/([0-9][0-9])/([0-9][0-9])</td>"
-						     1 2 3)
-					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
-					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
-					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1))
-					 result))))
-	    (or (and (= 1 (tbl-ref->number tbl 0 1))
-			 (= 0 (tbl-ref->number tbl 0 2))
-			 (= 1 (tbl-ref->number tbl 0 3))
-			 (= 1 (tbl-row-count tbl))
-			 (= 4 (tbl-column-count tbl)))
-                (begin (format #t "Single-txn test ~a failed~%" uuid) #f))
-                ))))))
-
-
-(define (two-txn-test uuid)
-  (let* ((template (gnc:find-report-template uuid))
-	 (options (gnc:make-report-options uuid))
-	 (report (constructor uuid "bar" options
-				     #t #t #f #f ""))
-	 (renderer (gnc:report-template-renderer template)))
-    (let* ((env (create-test-env))
-	   (my-asset-account (env-create-root-account env ACCT-TYPE-ASSET
-						      (gnc-default-report-currency)))
-	   (my-expense-account (env-create-root-account env ACCT-TYPE-EXPENSE
-							(gnc-default-report-currency)))
-	   (my-income-account (env-create-root-account env ACCT-TYPE-INCOME
-						       (gnc-default-report-currency)))
-	   (date-0 (gnc:get-start-this-month))
-	   (date-1 (gnc:time64-next-day date-0))
-	   (date-2 (gnc:time64-next-day date-1)))
-      (env-create-transaction env
-			       date-1
-			       my-income-account
-			       my-asset-account
-			       -1/1)
-      (env-create-transaction env
-			       date-2
-			       my-income-account
-			       my-asset-account
-			       -5/1)
-      (begin
-	(set-option report gnc:pagename-display "Show table" #t)
-	(set-option report gnc:pagename-general "Start Date" (cons 'absolute date-0))
-	(set-option report gnc:pagename-general "End Date" (cons 'absolute date-2))
-	(set-option report gnc:pagename-general "Step Size" 'DayDelta)
-	(set-option report gnc:pagename-general "Price Source" 'pricedb-nearest)
-	(set-option report gnc:pagename-general "Report's currency"  (gnc-default-report-currency))
-	(set-option report gnc:pagename-accounts "Accounts" (list my-asset-account))
-
-	(let ((doc (renderer report)))
-	  (gnc:html-document-set-style-sheet! doc
-					      (gnc:report-stylesheet report))
-	  (let* ((result (gnc:html-document-render doc #f))
-		 (tbl (stream->list
-		       (pattern-streamer "<tr>"
-					 (list (list "<td>([0-9][0-9])/([0-9][0-9])/([0-9][0-9])</td>"
-						     1 2 3)
-					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
-					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
-					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1))
-					 result))))
-	    (or (and (every (lambda (row)
-                              (and (or (equal? (second row) (fourth row))
-                                       (begin (format "Second Element ~g != fourth element ~g~%" (second row) (fourth row)) #f))
-                                   (or (= 0 (string->number (car (third row))))
-                                       (begin (format "third row element ~a not 0~%" (car (third row))) #f))))
-				tbl)
-                     (or (= 0 (tbl-ref->number tbl 0 1))
-                         (begin (format #t "Item 1 failed: ~g not 0~%" (tbl-ref->number tbl 0 1)) #f))
-                     (or (= 1 (tbl-ref->number tbl 1 1))
-                         (begin (format #t "Item 1 failed: ~g not 1~%" (tbl-ref->number tbl 1 1)) #f))
-                     (or (= 6 (tbl-ref->number tbl 2 1))
-                         (begin (format #t "Item 2 failed: ~g not 6~%" (tbl-ref->number tbl 2 1)) #f))
-                     (or (= 3 (tbl-row-count tbl))
-                         (begin (format #t "Item 3 failed: ~g not 3~%" (tbl-row-count tbl)) #f))
-                     (or (= 4 (tbl-column-count tbl))
-                         (begin (format #t "Item 4 failed: ~g not 4~%" (tbl-column-count tbl)) #f)))
-                (begin (format #t "Two-txn test ~a failed~%" uuid) #f))
-                ))))))
-
-
-(define (two-txn-test-2 uuid)
-  (let* ((template (gnc:find-report-template uuid))
-	 (options (gnc:make-report-options uuid))
-	 (report (constructor uuid "bar" options
-				     #t #t #f #f ""))
-	 (renderer (gnc:report-template-renderer template)))
-    (let* ((env (create-test-env))
-	   (my-asset-account (env-create-root-account env ACCT-TYPE-ASSET
-						      (gnc-default-report-currency)))
-	   (my-liability-account (env-create-root-account env ACCT-TYPE-LIABILITY
-						      (gnc-default-report-currency)))
-	   (my-expense-account (env-create-root-account env ACCT-TYPE-EXPENSE
-							(gnc-default-report-currency)))
-	   (my-income-account (env-create-root-account env ACCT-TYPE-INCOME
-						       (gnc-default-report-currency)))
-	   (date-0 (gnc:get-start-this-month))
-	   (date-1 (gnc:time64-next-day date-0))
-	   (date-2 (gnc:time64-next-day date-1)))
-      (env-create-transaction env date-1 my-income-account my-asset-account -1/1)
-      (env-create-transaction env date-1 my-expense-account my-liability-account -1/1)
-      (env-create-transaction env date-2 my-income-account my-asset-account -5/1)
-      (env-create-transaction env date-2 my-expense-account my-liability-account -5/1)
-      (begin
-	(set-option report gnc:pagename-display "Show table" #t)
-	(set-option report gnc:pagename-general "Start Date" (cons 'absolute date-0))
-	(set-option report gnc:pagename-general "End Date" (cons 'absolute date-2))
-	(set-option report gnc:pagename-general "Step Size" 'DayDelta)
-	(set-option report gnc:pagename-general "Price Source" 'pricedb-nearest)
-	(set-option report gnc:pagename-general "Report's currency"  (gnc-default-report-currency))
-	(set-option report gnc:pagename-accounts "Accounts" (list my-asset-account my-liability-account))
-
-	(let ((doc (renderer report)))
-	  (gnc:html-document-set-style-sheet! doc
-					      (gnc:report-stylesheet report))
-	  (let* ((result (gnc:html-document-render doc #f))
-		 (tbl (stream->list
-		       (pattern-streamer "<tr>"
-					 (list (list "<td>([0-9][0-9])/([0-9][0-9])/([0-9][0-9])</td>"
-						     1 2 3)
-					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
-					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
-					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1))
-					 result))))
-	    (or (and (every (lambda (row)
-				  (and (= (string->number (car (fourth row)))
-					  (+ (string->number (car (second row)))
-					     (string->number (car (third row)))))
-				       ;; txns added in pairs, so assets = liability
-				       (equal? (second row) (third row))))
-				tbl)
-			 (= 0 (tbl-ref->number tbl 0 1))
-			 (= 1 (tbl-ref->number tbl 1 1))
-			 (= 6 (tbl-ref->number tbl 2 1))
-			 (= 3 (tbl-row-count tbl))
-			 (= 4 (tbl-column-count tbl)))
-                (begin (format #t "two-txn test 2 ~a failed~%" uuid) #f))
-                ))))))
-
-(define (two-txn-test-income uuid)
-  (let* ((template (gnc:find-report-template uuid))
-	 (options (gnc:make-report-options uuid))
-	 (report (constructor uuid "bar" options
-				     #t #t #f #f ""))
-	 (renderer (gnc:report-template-renderer template)))
-    (let* ((env (create-test-env))
-	   (my-asset-account (env-create-root-account env ACCT-TYPE-ASSET
-						      (gnc-default-report-currency)))
-	   (my-liability-account (env-create-root-account env ACCT-TYPE-LIABILITY
-						      (gnc-default-report-currency)))
-	   (my-expense-account (env-create-root-account env ACCT-TYPE-EXPENSE
-							(gnc-default-report-currency)))
-	   (my-income-account (env-create-root-account env ACCT-TYPE-INCOME
-						       (gnc-default-report-currency)))
-	   (date-0 (gnc:get-start-this-month))
-	   (date-1 (gnc:time64-next-day date-0))
-	   (date-2 (gnc:time64-next-day date-1)))
-      (env-create-transaction env date-1 my-income-account my-asset-account -1/1)
-      (env-create-transaction env date-1 my-expense-account my-liability-account -1/1)
-      (env-create-transaction env date-2 my-income-account my-asset-account -5/1)
-      (env-create-transaction env date-2 my-expense-account my-liability-account -5/1)
-      (begin
-	(set-option report gnc:pagename-display "Show table" #t)
-	(set-option report gnc:pagename-general "Start Date" (cons 'absolute date-0))
-	(set-option report gnc:pagename-general "End Date" (cons 'absolute date-2))
-	(set-option report gnc:pagename-general "Step Size" 'DayDelta)
-	(set-option report gnc:pagename-general "Price Source" 'pricedb-nearest)
-	(set-option report gnc:pagename-general "Report's currency"  (gnc-default-report-currency))
-	(set-option report gnc:pagename-accounts "Accounts" (list my-income-account my-expense-account))
-
-	(let ((doc (renderer report)))
-	  (gnc:html-document-set-style-sheet! doc
-					      (gnc:report-stylesheet report))
-	  (let* ((result (gnc:html-document-render doc #f))
-		 (tbl (stream->list
-		       (pattern-streamer "<tr>"
-					 (list (list "<td>([0-9][0-9])/([0-9][0-9])/([0-9][0-9])</td>"
-						     1 2 3)
-					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
-					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
-					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1))
-					 result))))
-	    (or (and (every (lambda (row)
-				  (and (= (string->number (car (fourth row)))
-					  (+ (string->number (car (second row)))
-					     (string->number (car (third row)))))
-				       ;; txns added in pairs, so assets = liability
-				       (equal? (second row) (third row))))
-				tbl)
-			 (= 0 (tbl-ref->number tbl 0 1))
-			 (= 1 (tbl-ref->number tbl 1 1))
-			 (= 5 (tbl-ref->number tbl 2 1))
-			 (= 3 (tbl-row-count tbl))
-			 (= 4 (tbl-column-count tbl)))
-                (begin (format #t "two-txn-income test ~a failed~%" uuid) #f))
-                ))))))
-
-
-(define (closing-test uuid)
-  (let* ((template (gnc:find-report-template uuid))
-	 (options (gnc:make-report-options uuid))
-	 (report (constructor uuid "bar" options
-			      #t #t #f #f ""))
-	 (renderer (gnc:report-template-renderer template)))
-    (let* ((env (create-test-env))
-	   (my-asset-account (env-create-root-account env ACCT-TYPE-ASSET
-						      (gnc-default-report-currency)))
-	   (my-liability-account (env-create-root-account env ACCT-TYPE-LIABILITY
-						      (gnc-default-report-currency)))
-	   (my-expense-account (env-create-root-account env ACCT-TYPE-EXPENSE
-							(gnc-default-report-currency)))
-	   (my-income-account (env-create-root-account env ACCT-TYPE-INCOME
-						       (gnc-default-report-currency)))
-	   (my-equity-account (env-create-root-account env ACCT-TYPE-EQUITY
-						       (gnc-default-report-currency)))
-	   (date-0 (gnc:get-start-this-month))
-	   (date-1 (gnc:time64-next-day date-0))
-	   (date-2 (gnc:time64-next-day date-1))
-	   (date-3 (gnc:time64-next-day date-2)))
-
-      (env-create-transaction env date-1 my-income-account my-asset-account -1/1)
-      (env-create-transaction env date-2 my-income-account my-asset-account -2/1)
-      (env-create-transaction env date-3 my-income-account my-asset-account -3/1)
-
-      (let ((closing-txn (env-create-transaction env date-2 my-asset-account my-equity-account
-						 300/1)))
-	(xaccTransSetIsClosingTxn closing-txn #t))
-
-      (begin
-	(set-option report gnc:pagename-display "Show table" #t)
-	(set-option report gnc:pagename-general "Start Date" (cons 'absolute date-0))
-	(set-option report gnc:pagename-general "End Date" (cons 'absolute date-3))
-	(set-option report gnc:pagename-general "Step Size" 'DayDelta)
-	(set-option report gnc:pagename-general "Price Source" 'pricedb-nearest)
-	(set-option report gnc:pagename-general "Report's currency"  (gnc-default-report-currency))
-	(set-option report gnc:pagename-accounts "Accounts" (list my-income-account my-expense-account))
-
-	(let ((doc (renderer report)))
-	  (gnc:html-document-set-style-sheet! doc
-					      (gnc:report-stylesheet report))
-	  (let* ((result (gnc:html-document-render doc #f))
-		 (tbl (stream->list
-		       (pattern-streamer "<tr>"
-					 (list (list "<td>([0-9][0-9])/([0-9][0-9])/([0-9][0-9])</td>"
-						     1 2 3)
-					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
-					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
-					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1))
-					 result))))
-	    (or (and (every (lambda (row)
-				  (and (= (string->number (car (fourth row)))
-					  (+ (string->number (car (second row)))
-					     (string->number (car (third row)))))))
-				tbl)
-			 (= 0 (tbl-ref->number tbl 0 1))
-			 (= 1 (tbl-ref->number tbl 1 1))
-			 (= 2 (tbl-ref->number tbl 2 1))
-			 (= 3 (tbl-ref->number tbl 3 1))
-			 (= 4 (tbl-row-count tbl))
-			 (= 4 (tbl-column-count tbl)))
-                (begin (format #t "Closing-txn test ~a failed~%" uuid) #f))
-                ))))))
-
diff --git a/gnucash/report/standard-reports/test/test-generic-net-linechart.scm b/gnucash/report/standard-reports/test/test-generic-net-linechart.scm
deleted file mode 100644
index a59197a..0000000
--- a/gnucash/report/standard-reports/test/test-generic-net-linechart.scm
+++ /dev/null
@@ -1,225 +0,0 @@
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; This program is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU General Public License as
-;; published by the Free Software Foundation; either version 2 of
-;; the License, or (at your option) any later version.
-;;
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-;;
-;; You should have received a copy of the GNU General Public License
-;; along with this program; if not, contact:
-;;
-;; Free Software Foundation           Voice:  +1-617-542-5942
-;; 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
-;; Boston, MA  02110-1301,  USA       gnu at gnu.org
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(define-module (gnucash report standard-reports test test-generic-net-linechart))
-
-(use-modules (ice-9 format))
-(use-modules (ice-9 streams))
-(use-modules (srfi srfi-1))
-
-(use-modules (gnucash gnc-module))
-(gnc:module-load "gnucash/report/report-system" 0)
-(use-modules (gnucash report stylesheets))
-
-(use-modules (gnucash report report-system test test-extras))
-(use-modules (gnucash engine test test-extras))
-
-(export run-net-asset-test)
-
-(define (set-option report page tag value)
-  ((gnc:option-setter (gnc:lookup-option (gnc:report-options report)
-					 page tag)) value))
-
-
-(define constructor (record-constructor <report>))
-
-(define (run-net-asset-test asset-report-uuid)
-  (and (two-txn-test asset-report-uuid)
-       (two-txn-test-2 asset-report-uuid)
-
-       (null-test asset-report-uuid)
-       (single-txn-test asset-report-uuid)))
-
-;; Just prove that the report exists.
-(define (null-test uuid)
-  (let* ((template (gnc:find-report-template uuid))
-	 (options (gnc:make-report-options uuid))
-	 (report (constructor uuid "bar" options
-				     #t #t #f #f ""))
-	 (renderer (gnc:report-template-renderer template)))
-
-    (let ((doc (renderer report)))
-      (gnc:html-document-set-style-sheet! doc
-					  (gnc:report-stylesheet report))
-      ;;(format #t "render: ~a\n" (gnc:html-document-render doc #f))
-      #t
-      )))
-
-(define (single-txn-test uuid)
-  (let* ((template (gnc:find-report-template uuid))
-	 (options (gnc:make-report-options uuid))
-	 (report (constructor uuid "bar" options
-				     #t #t #f #f ""))
-	 (renderer (gnc:report-template-renderer template)))
-    (let* ((env (create-test-env))
-	   (my-asset-account (env-create-root-account env ACCT-TYPE-ASSET
-						      (gnc-default-report-currency)))
-	   (my-expense-account (env-create-root-account env ACCT-TYPE-EXPENSE
-							(gnc-default-report-currency)))
-	   (my-income-account (env-create-root-account env ACCT-TYPE-INCOME
-						       (gnc-default-report-currency))))
-      (env-create-transaction env
-			       (gnc:get-start-this-month)
-			       my-income-account
-			       my-asset-account
-			       -1/1)
-      (begin
-	(set-option report gnc:pagename-display "Show table" #t)
-	(set-option report gnc:pagename-general "Start Date"
-		    (cons 'absolute (gnc:get-start-this-month)))
-	(set-option report gnc:pagename-general "End Date"
-		    (cons 'absolute (gnc:get-start-this-month)))
-	(set-option report gnc:pagename-general "Step Size" 'DayDelta)
-	(set-option report gnc:pagename-general "Price Source" 'pricedb-nearest)
-	(set-option report gnc:pagename-general "Report's currency"  (gnc-default-report-currency))
-	(set-option report gnc:pagename-accounts "Accounts" (list my-asset-account))
-
-	(let ((doc (renderer report)))
-	  (gnc:html-document-set-style-sheet! doc
-					      (gnc:report-stylesheet report))
-	  (let* ((result (gnc:html-document-render doc #f))
-		 (tbl (stream->list
-		       (pattern-streamer "<tr>"
-					 (list (list "<td>([0-9][0-9])/([0-9][0-9])/([0-9][0-9])</td>"
-						     1 2 3)
-					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
-					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
-					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1))
-					 result))))
-	    (and (= 1 (tbl-ref->number tbl 0 1))
-			 (= 0 (tbl-ref->number tbl 0 2))
-			 (= 1 (tbl-ref->number tbl 0 3))
-			 (= 1 (tbl-row-count tbl))
-			 (= 4 (tbl-column-count tbl)))))))))
-
-
-(define (two-txn-test uuid)
-  (let* ((template (gnc:find-report-template uuid))
-	 (options (gnc:make-report-options uuid))
-	 (report (constructor uuid "bar" options
-				     #t #t #f #f ""))
-	 (renderer (gnc:report-template-renderer template)))
-    (let* ((env (create-test-env))
-	   (my-asset-account (env-create-root-account env ACCT-TYPE-ASSET
-						      (gnc-default-report-currency)))
-	   (my-expense-account (env-create-root-account env ACCT-TYPE-EXPENSE
-							(gnc-default-report-currency)))
-	   (my-income-account (env-create-root-account env ACCT-TYPE-INCOME
-						       (gnc-default-report-currency)))
-	   (date-0 (gnc:get-start-this-month))
-	   (date-1 (gnc:time64-next-day date-0))
-	   (date-2 (gnc:time64-next-day date-1)))
-      (env-create-transaction env
-			       date-1
-			       my-income-account
-			       my-asset-account
-			       -1/1)
-      (env-create-transaction env
-			       date-2
-			       my-income-account
-			       my-asset-account
-			       -5/1)
-      (begin
-	(set-option report gnc:pagename-display "Show table" #t)
-	(set-option report gnc:pagename-general "Start Date" (cons 'absolute date-0))
-	(set-option report gnc:pagename-general "End Date" (cons 'absolute date-2))
-	(set-option report gnc:pagename-general "Step Size" 'DayDelta)
-	(set-option report gnc:pagename-general "Price Source" 'pricedb-nearest)
-	(set-option report gnc:pagename-general "Report's currency"  (gnc-default-report-currency))
-	(set-option report gnc:pagename-accounts "Accounts" (list my-asset-account))
-
-	(let ((doc (renderer report)))
-	  (gnc:html-document-set-style-sheet! doc
-					      (gnc:report-stylesheet report))
-	  (let* ((result (gnc:html-document-render doc #f))
-		 (tbl (stream->list
-		       (pattern-streamer "<tr>"
-					 (list (list "<td>([0-9][0-9])/([0-9][0-9])/([0-9][0-9])</td>"
-						     1 2 3)
-					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
-					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
-					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1))
-					 result))))
-	    (and (every (lambda (row)
-				  (and (equal? (second row) (fourth row))
-				       (= 0 (string->number (car (third row))))))
-				tbl)
-			 (= 0 (tbl-ref->number tbl 0 1))
-			 (= 1 (tbl-ref->number tbl 1 1))
-			 (= 6 (tbl-ref->number tbl 2 1))
-			 (= 3 (tbl-row-count tbl))
-			 (= 4 (tbl-column-count tbl)))))))))
-
-
-(define (two-txn-test-2 uuid)
-  (let* ((template (gnc:find-report-template uuid))
-	 (options (gnc:make-report-options uuid))
-	 (report (constructor uuid "bar" options
-				     #t #t #f #f ""))
-	 (renderer (gnc:report-template-renderer template)))
-    (let* ((env (create-test-env))
-	   (my-asset-account (env-create-root-account env ACCT-TYPE-ASSET
-						      (gnc-default-report-currency)))
-	   (my-liability-account (env-create-root-account env ACCT-TYPE-LIABILITY
-						      (gnc-default-report-currency)))
-	   (my-expense-account (env-create-root-account env ACCT-TYPE-EXPENSE
-							(gnc-default-report-currency)))
-	   (my-income-account (env-create-root-account env ACCT-TYPE-INCOME
-						       (gnc-default-report-currency)))
-	   (date-0 (gnc:get-start-this-month))
-	   (date-1 (gnc:time64-next-day date-0))
-	   (date-2 (gnc:time64-next-day date-1)))
-      (env-create-transaction env date-1 my-income-account my-asset-account -1/1)
-      (env-create-transaction env date-1 my-expense-account my-liability-account -1/1)
-      (env-create-transaction env date-2 my-income-account my-asset-account -5/1)
-      (env-create-transaction env date-2 my-expense-account my-liability-account -5/1)
-      (begin
-	(set-option report gnc:pagename-display "Show table" #t)
-	(set-option report gnc:pagename-general "Start Date" (cons 'absolute date-0))
-	(set-option report gnc:pagename-general "End Date" (cons 'absolute date-2))
-	(set-option report gnc:pagename-general "Step Size" 'DayDelta)
-	(set-option report gnc:pagename-general "Price Source" 'pricedb-nearest)
-	(set-option report gnc:pagename-general "Report's currency"  (gnc-default-report-currency))
-	(set-option report gnc:pagename-accounts "Accounts" (list my-asset-account my-liability-account))
-
-	(let ((doc (renderer report)))
-	  (gnc:html-document-set-style-sheet! doc
-					      (gnc:report-stylesheet report))
-	  (let* ((result (gnc:html-document-render doc #f))
-		 (tbl (stream->list
-		       (pattern-streamer "<tr>"
-					 (list (list "<td>([0-9][0-9])/([0-9][0-9])/([0-9][0-9])</td>"
-						     1 2 3)
-					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
-					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
-					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1))
-					 result))))
-	    (and (every (lambda (row)
-				  (and (= (string->number (car (fourth row)))
-					  (+ (string->number (car (second row)))
-					     (string->number (car (third row)))))
-				       ;; txns added in pairs, so assets = liability
-				       (equal? (second row) (third row))))
-				tbl)
-			 (= 0 (tbl-ref->number tbl 0 1))
-			 (= 1 (tbl-ref->number tbl 1 1))
-			 (= 6 (tbl-ref->number tbl 2 1))
-			 (= 3 (tbl-row-count tbl))
-			 (= 4 (tbl-column-count tbl)))))))))
-
diff --git a/gnucash/report/standard-reports/test/test-standard-category-report.scm b/gnucash/report/standard-reports/test/test-standard-category-report.scm
index cdb2641..ace0e9b 100644
--- a/gnucash/report/standard-reports/test/test-standard-category-report.scm
+++ b/gnucash/report/standard-reports/test/test-standard-category-report.scm
@@ -36,11 +36,16 @@
 (use-modules (gnucash engine))
 (use-modules (sw_engine))
 (use-modules (gnucash report standard-reports net-charts))
-
 (use-modules (gnucash report report-system test test-extras))
-
-(use-modules (gnucash report standard-reports test test-generic-category-report))
 (use-modules (gnucash report standard-reports category-barchart))
+(use-modules (ice-9 format))
+(use-modules (ice-9 streams))
+(use-modules (srfi srfi-1))
+(use-modules (gnucash report stylesheets))
+(use-modules (gnucash report report-system collectors))
+(use-modules (gnucash engine test test-extras))
+(use-modules (gnucash report report-system test test-extras))
+
 
 ;; Explicitly set locale to make the report output predictable
 (setlocale LC_ALL "C")
@@ -48,3 +53,257 @@
 (define (run-test)
   (run-category-income-expense-test category-barchart-income-uuid category-barchart-expense-uuid)
   (run-category-asset-liability-test category-barchart-asset-uuid category-barchart-liability-uuid))
+
+(export run-category-income-expense-test)
+(export run-category-asset-liability-test)
+
+(define (set-option report page tag value)
+  ((gnc:option-setter (gnc:lookup-option (gnc:report-options report)
+					 page tag)) value))
+
+
+(define constructor (record-constructor <report>))
+
+;(set-option income-report gnc:pagename-general "Start Date" (cons 'relative 'start-prev-year))
+;(set-option income-report gnc:pagename-general "End Date" (cons 'relative 'end-this-month))
+;(set-option income-report gnc:pagename-general "Show table" #t)
+;(set-option income-report gnc:pagename-general "Price Source" 'pricedb-nearest)
+;(set-option income-report gnc:pagename-general "Report's currency"  (gnc-default-report-currency))
+
+(define (run-category-income-expense-test income-report-uuid expense-report-uuid)
+  (and  (null-test income-report-uuid)
+	(null-test expense-report-uuid)
+	(single-txn-test income-report-uuid)
+	(multi-acct-test expense-report-uuid)
+	#t))
+
+(define (run-category-asset-liability-test asset-report-uuid liability-report-uuid)
+  (and (null-test asset-report-uuid)
+       (null-test liability-report-uuid)
+       (asset-test asset-report-uuid)
+       (liability-test liability-report-uuid)
+       #t))
+
+;; No real test here, just confirm that no exceptions are thrown
+(define (null-test uuid)
+  (let* ((template (gnc:find-report-template uuid))
+	 (options (gnc:make-report-options uuid))
+	 (report (constructor uuid "bar" options
+				     #t #t #f #f ""))
+	 (renderer (gnc:report-template-renderer template)))
+
+    (let ((doc (renderer report)))
+      (gnc:html-document-set-style-sheet! doc
+					  (gnc:report-stylesheet report))
+      #t
+      )))
+
+
+(define (single-txn-test uuid)
+  (let* ((income-template (gnc:find-report-template uuid))
+	 (income-options (gnc:make-report-options uuid))
+	 (income-report (constructor uuid "bar" income-options
+				     #t #t #f #f ""))
+	 (income-renderer (gnc:report-template-renderer income-template)))
+    (let* ((env (create-test-env))
+	   (my-asset-account (env-create-root-account env ACCT-TYPE-ASSET
+						      (gnc-default-report-currency)))
+	   (my-expense-account (env-create-root-account env ACCT-TYPE-EXPENSE
+							(gnc-default-report-currency)))
+	   (my-income-account (env-create-root-account env ACCT-TYPE-INCOME
+						       (gnc-default-report-currency))))
+      (env-create-daily-transactions env
+				     (gnc:get-start-this-month)
+				     (gnc:get-end-this-month)
+				     my-asset-account my-income-account)
+      (begin
+	(set-option income-report gnc:pagename-display "Show table" #t)
+	(set-option income-report gnc:pagename-general "Start Date" (cons 'relative 'start-this-month))
+	(set-option income-report gnc:pagename-general "End Date" (cons 'relative 'end-this-month))
+	(set-option income-report gnc:pagename-general "Step Size" 'DayDelta)
+	(set-option income-report gnc:pagename-general "Price Source" 'pricedb-nearest)
+	(set-option income-report gnc:pagename-general "Report's currency"  (gnc-default-report-currency))
+	(set-option income-report gnc:pagename-accounts "Accounts" (list my-income-account))
+	(set-option income-report gnc:pagename-accounts "Show Accounts until level"  'all)
+
+	(let ((doc (income-renderer income-report)))
+	  (gnc:html-document-set-style-sheet! doc
+					      (gnc:report-stylesheet income-report))
+	  (let* ((result (gnc:html-document-render doc #f))
+		 (tbl (stream->list
+		       (pattern-streamer "<tr>"
+					 (list (list "<td>([0-9][0-9])/([0-9][0-9])/([0-9][0-9])</td>" 1 2 3)
+					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1))
+					 result))))
+	    (every (lambda (date value-list)
+		     (let ((day (second date))
+			   (value (first value-list)))
+		       (= (string->number day) (string->number value))))
+		   (map first tbl)
+		   (map second tbl))))))))
+
+(define (list-leaves list)
+  (if (not (pair? list))
+      (cons list '())
+      (fold (lambda (next acc)
+	      (append (list-leaves next)
+		      acc))
+	    '()
+	    list)))
+
+(define (multi-acct-test expense-report-uuid)
+  (let* ((expense-template (gnc:find-report-template expense-report-uuid))
+	 (expense-options (gnc:make-report-options expense-report-uuid))
+	 (expense-report (constructor expense-report-uuid "bar" expense-options
+				     #t #t #f #f ""))
+	 (expense-renderer (gnc:report-template-renderer expense-template)))
+    (let* ((env (create-test-env))
+	   (expense-accounts (env-expense-account-structure env))
+	   (asset-accounts (env-create-account-structure
+			    env
+			    (list "Assets"
+				  (list (cons 'type ACCT-TYPE-ASSET))
+				  (list "Bank"))))
+	   (leaf-expense-accounts (list-leaves expense-accounts))
+	   (bank-account (car (car (cdr asset-accounts)))))
+      (for-each (lambda (expense-account)
+		  (env-create-daily-transactions env
+						 (gnc:get-start-this-month)
+						 (gnc:get-end-this-month)
+						 expense-account
+						 bank-account))
+		leaf-expense-accounts)
+      (begin
+	(set-option expense-report gnc:pagename-display "Show table" #t)
+	(set-option expense-report gnc:pagename-general "Start Date" (cons 'relative 'start-this-month))
+	(set-option expense-report gnc:pagename-general "End Date" (cons 'relative 'end-this-month))
+	(set-option expense-report gnc:pagename-general "Step Size" 'DayDelta)
+	(set-option expense-report gnc:pagename-general "Price Source" 'pricedb-nearest)
+	(set-option expense-report gnc:pagename-general "Report's currency"  (gnc-default-report-currency))
+	(set-option expense-report gnc:pagename-accounts "Accounts" leaf-expense-accounts)
+	(set-option expense-report gnc:pagename-accounts "Show Accounts until level" 2)
+
+	(let ((doc (expense-renderer expense-report)))
+	  (gnc:html-document-set-style-sheet! doc
+					      (gnc:report-stylesheet expense-report))
+	  (let* ((html-document (gnc:html-document-render doc #f))
+		 (columns (columns-from-report-document html-document))
+		 (tbl (stream->list
+		       (pattern-streamer "<tr>"
+					 (list (list "<td>([0-9][0-9])/([0-9][0-9])/([0-9][0-9])</td>" 1 2 3)
+					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
+					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
+					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
+					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
+					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1))
+					 html-document))))
+	    ;(format #t "~a" html-document)
+	    (and (= 6 (length columns))
+		 (equal? "Date" (first columns))
+		 (equal? "Auto" (second columns))
+		 ;; maybe should try to check actual values
+		 )))))))
+
+(define (columns-from-report-document doc)
+  (let ((columns (stream->list (pattern-streamer "<th>"
+						 (list (list "<th>([^<]*)</" 1))
+						 doc))))
+    (map caar columns)))
+
+;;
+;;
+;;
+
+(define (asset-test uuid)
+    (let* ((asset-template (gnc:find-report-template uuid))
+	   (asset-options (gnc:make-report-options uuid))
+	   (asset-report (constructor uuid "bar" asset-options
+				      #t #t #f #f ""))
+	   (asset-renderer (gnc:report-template-renderer asset-template)))
+      (let* ((env (create-test-env))
+	     (my-asset-account (env-create-root-account env ACCT-TYPE-ASSET
+							(gnc-default-report-currency)))
+	     (my-expense-account (env-create-root-account env ACCT-TYPE-EXPENSE
+							  (gnc-default-report-currency)))
+	     (my-income-account (env-create-root-account env ACCT-TYPE-INCOME
+							 (gnc-default-report-currency))))
+      (env-create-daily-transactions env
+				     (gnc:get-start-this-month)
+				     (gnc:get-end-this-month)
+				     my-asset-account my-income-account)
+      (begin
+	(set-option asset-report gnc:pagename-display "Show table" #t)
+	(set-option asset-report gnc:pagename-general "Start Date" (cons 'relative 'start-this-month))
+	(set-option asset-report gnc:pagename-general "End Date" (cons 'relative 'end-this-month))
+	(set-option asset-report gnc:pagename-general "Step Size" 'DayDelta)
+	(set-option asset-report gnc:pagename-general "Price Source" 'pricedb-nearest)
+	(set-option asset-report gnc:pagename-general "Price Source" 'pricedb-nearest)
+	(set-option asset-report gnc:pagename-general "Report's currency"  (gnc-default-report-currency))
+	(set-option asset-report gnc:pagename-accounts "Accounts" (list my-asset-account))
+	(set-option asset-report gnc:pagename-accounts "Show Accounts until level"  'all)
+
+	(let ((doc (asset-renderer asset-report)))
+	  (gnc:html-document-set-style-sheet! doc
+					      (gnc:report-stylesheet asset-report))
+	  (let* ((html-document (gnc:html-document-render doc #f))
+		 (columns (columns-from-report-document html-document))
+		 (tbl (stream->list
+		       (pattern-streamer "<tr>"
+					 (list (list "<td>([0-9][0-9])/([0-9][0-9])/([0-9][0-9])</td>" 1 2 3)
+					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1))
+					 html-document)))
+		 (row-count (tbl-row-count tbl)))
+	    (and (member "account-1" columns)
+			 (= 2 (length columns))
+			 (= 1 (string->number (car (tbl-ref tbl 0 1))))
+			 (= (/ (* row-count (+ row-count 1)) 2)
+			    (string->number (car (tbl-ref tbl (- row-count 1) 1))))
+			 #t)))))))
+
+(define (liability-test uuid)
+  ;; this test is tailored for bug 793278
+  ;; except we can't use $10,000 because the string->number
+  ;; function cannot handle thousand separators. Use $100.
+  (let* ((liability-template (gnc:find-report-template uuid))
+         (liability-options (gnc:make-report-options uuid))
+         (liability-report (constructor uuid "bar" liability-options
+                                        #t #t #f #f ""))
+         (liability-renderer (gnc:report-template-renderer liability-template)))
+    (let* ((env (create-test-env))
+           (asset--acc (env-create-root-account env ACCT-TYPE-ASSET (gnc-default-report-currency)))
+           (liabil-acc (env-create-root-account env ACCT-TYPE-CREDIT (gnc-default-report-currency)))
+           (income-acc (env-create-root-account env ACCT-TYPE-INCOME (gnc-default-report-currency))))
+      (env-create-transaction env (gnc-dmy2time64 01 10 2016) asset--acc liabil-acc 100) ;loan
+      (env-create-transaction env (gnc-dmy2time64 01 01 2017) asset--acc income-acc 10)  ;salary#1
+      (env-create-transaction env (gnc-dmy2time64 02 01 2017) liabil-acc asset--acc 9)   ;repay#1
+      (env-create-transaction env (gnc-dmy2time64 01 02 2017) asset--acc income-acc 10)  ;salary#2
+      (env-create-transaction env (gnc-dmy2time64 02 02 2017) liabil-acc asset--acc 9)   ;repay#2
+      (env-create-transaction env (gnc-dmy2time64 01 03 2017) asset--acc income-acc 10)  ;salary#3
+      (env-create-transaction env (gnc-dmy2time64 02 03 2017) liabil-acc asset--acc 9)   ;repay#3
+      (env-create-transaction env (gnc-dmy2time64 01 04 2017) asset--acc income-acc 10)  ;salary#4
+      (env-create-transaction env (gnc-dmy2time64 02 04 2017) liabil-acc asset--acc 9)   ;repay#4
+      (env-create-transaction env (gnc-dmy2time64 01 05 2017) asset--acc income-acc 10)  ;salary#5
+      (env-create-transaction env (gnc-dmy2time64 02 05 2017) liabil-acc asset--acc 9)   ;repay#5
+      (begin
+        (set-option liability-report gnc:pagename-display "Show table" #t)
+        (set-option liability-report gnc:pagename-general "Start Date" (cons 'absolute (gnc-dmy2time64 01 01 2017)))
+        (set-option liability-report gnc:pagename-general "End Date" (cons 'absolute (gnc-dmy2time64 31 12 2018)))
+        (set-option liability-report gnc:pagename-general "Step Size" 'MonthDelta)
+        (set-option liability-report gnc:pagename-general "Price Source" 'pricedb-nearest)
+        (set-option liability-report gnc:pagename-general "Report's currency"  (gnc-default-report-currency))
+        (set-option liability-report gnc:pagename-accounts "Accounts" (list liabil-acc))
+        (set-option liability-report gnc:pagename-accounts "Show Accounts until level"  'all)
+        (let ((doc (liability-renderer liability-report)))
+          (gnc:html-document-set-style-sheet! doc (gnc:report-stylesheet liability-report))
+          (let* ((html-document (gnc:html-document-render doc #f))
+                 (columns (columns-from-report-document html-document))
+                 (tbl (stream->list
+                       (pattern-streamer "<tr>"
+                                         (list (list "<td>([0-9][0-9])/([0-9][0-9])/([0-9][0-9])</td>" 1 2 3)
+                                               (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1))
+                                         html-document)))
+                 (row-count (tbl-row-count tbl)))
+            (and (= 2 (length columns))
+                 (= 100 (string->number (car (tbl-ref tbl 0 1))))
+                 (= 55 (string->number (car (tbl-ref tbl (- row-count 1) 1))))
+                 #t)))))))
diff --git a/gnucash/report/standard-reports/test/test-standard-net-barchart.scm b/gnucash/report/standard-reports/test/test-standard-net-barchart.scm
index 0ef9fd5..d62cb69 100644
--- a/gnucash/report/standard-reports/test/test-standard-net-barchart.scm
+++ b/gnucash/report/standard-reports/test/test-standard-net-barchart.scm
@@ -18,18 +18,17 @@
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 (debug-set! stack 50000)
-;(use-modules (gnucash report new-reports reports-2))
 
 (use-modules (gnucash gnc-module))
-
 (gnc:module-begin-syntax (gnc:module-load "gnucash/report/report-system" 0))
-
 (use-modules (gnucash engine))
 (use-modules (sw_engine))
-
+(use-modules (ice-9 format))
+(use-modules (ice-9 streams))
+(use-modules (srfi srfi-1))
+(use-modules (gnucash report stylesheets))
+(use-modules (gnucash engine test test-extras))
 (use-modules (gnucash report report-system test test-extras))
-
-(use-modules (gnucash report standard-reports test test-generic-net-barchart))
 (use-modules (gnucash report standard-reports net-charts))
 
 ;; Explicitly set locale to make the report output predictable
@@ -38,3 +37,334 @@
 (define (run-test)
   (run-net-asset-income-test net-worth-barchart-uuid income-expense-barchart-uuid))
 
+(define (set-option report page tag value)
+  ((gnc:option-setter (gnc:lookup-option (gnc:report-options report)
+					 page tag)) value))
+
+(define constructor (record-constructor <report>))
+
+(define (run-net-asset-income-test asset-report-uuid income-report-uuid)
+  (and (two-txn-test asset-report-uuid)
+       (two-txn-test-2 asset-report-uuid)
+       (two-txn-test-income income-report-uuid)
+
+       (null-test asset-report-uuid)
+       (null-test income-report-uuid)
+       (single-txn-test asset-report-uuid)
+       (closing-test income-report-uuid)
+       #t))
+
+;; Just prove that the report exists.
+(define (null-test uuid)
+  (let* ((template (gnc:find-report-template uuid))
+	 (options (gnc:make-report-options uuid))
+	 (report (constructor uuid "bar" options
+			      #t #t #f #f ""))
+	 (renderer (gnc:report-template-renderer template)))
+
+    (let ((doc (renderer report)))
+      (gnc:html-document-set-style-sheet! doc
+					  (gnc:report-stylesheet report))
+      ;;(format #t "render: ~a\n" (gnc:html-document-render doc #f))
+      #t
+      )))
+
+(define (single-txn-test uuid)
+  (let* ((template (gnc:find-report-template uuid))
+	 (options (gnc:make-report-options uuid))
+	 (report (constructor uuid "bar" options
+				     #t #t #f #f ""))
+	 (renderer (gnc:report-template-renderer template)))
+    (let* ((env (create-test-env))
+	   (my-asset-account (env-create-root-account env ACCT-TYPE-ASSET
+						      (gnc-default-report-currency)))
+	   (my-expense-account (env-create-root-account env ACCT-TYPE-EXPENSE
+							(gnc-default-report-currency)))
+	   (my-income-account (env-create-root-account env ACCT-TYPE-INCOME
+						       (gnc-default-report-currency))))
+      (env-create-transaction env
+			       (gnc:get-start-this-month)
+			       my-income-account
+			       my-asset-account
+			       -1/1)
+      (begin
+	(set-option report gnc:pagename-display "Show table" #t)
+	(set-option report gnc:pagename-general "Start Date"
+		    (cons 'absolute (gnc:get-start-this-month)))
+	(set-option report gnc:pagename-general "End Date"
+		    (cons 'absolute (gnc:get-start-this-month)))
+	(set-option report gnc:pagename-general "Step Size" 'DayDelta)
+	(set-option report gnc:pagename-general "Price Source" 'pricedb-nearest)
+	(set-option report gnc:pagename-general "Report's currency"  (gnc-default-report-currency))
+	(set-option report gnc:pagename-accounts "Accounts" (list my-asset-account))
+
+	(let ((doc (renderer report)))
+	  (gnc:html-document-set-style-sheet! doc
+					      (gnc:report-stylesheet report))
+	  (let* ((result (gnc:html-document-render doc #f))
+		 (tbl (stream->list
+		       (pattern-streamer "<tr>"
+					 (list (list "<td>([0-9][0-9])/([0-9][0-9])/([0-9][0-9])</td>"
+						     1 2 3)
+					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
+					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
+					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1))
+					 result))))
+	    (or (and (= 1 (tbl-ref->number tbl 0 1))
+			 (= 0 (tbl-ref->number tbl 0 2))
+			 (= 1 (tbl-ref->number tbl 0 3))
+			 (= 1 (tbl-row-count tbl))
+			 (= 4 (tbl-column-count tbl)))
+                (begin (format #t "Single-txn test ~a failed~%" uuid) #f))
+                ))))))
+
+
+(define (two-txn-test uuid)
+  (let* ((template (gnc:find-report-template uuid))
+	 (options (gnc:make-report-options uuid))
+	 (report (constructor uuid "bar" options
+				     #t #t #f #f ""))
+	 (renderer (gnc:report-template-renderer template)))
+    (let* ((env (create-test-env))
+	   (my-asset-account (env-create-root-account env ACCT-TYPE-ASSET
+						      (gnc-default-report-currency)))
+	   (my-expense-account (env-create-root-account env ACCT-TYPE-EXPENSE
+							(gnc-default-report-currency)))
+	   (my-income-account (env-create-root-account env ACCT-TYPE-INCOME
+						       (gnc-default-report-currency)))
+	   (date-0 (gnc:get-start-this-month))
+	   (date-1 (gnc:time64-next-day date-0))
+	   (date-2 (gnc:time64-next-day date-1)))
+      (env-create-transaction env
+			       date-1
+			       my-income-account
+			       my-asset-account
+			       -1/1)
+      (env-create-transaction env
+			       date-2
+			       my-income-account
+			       my-asset-account
+			       -5/1)
+      (begin
+	(set-option report gnc:pagename-display "Show table" #t)
+	(set-option report gnc:pagename-general "Start Date" (cons 'absolute date-0))
+	(set-option report gnc:pagename-general "End Date" (cons 'absolute date-2))
+	(set-option report gnc:pagename-general "Step Size" 'DayDelta)
+	(set-option report gnc:pagename-general "Price Source" 'pricedb-nearest)
+	(set-option report gnc:pagename-general "Report's currency"  (gnc-default-report-currency))
+	(set-option report gnc:pagename-accounts "Accounts" (list my-asset-account))
+
+	(let ((doc (renderer report)))
+	  (gnc:html-document-set-style-sheet! doc
+					      (gnc:report-stylesheet report))
+	  (let* ((result (gnc:html-document-render doc #f))
+		 (tbl (stream->list
+		       (pattern-streamer "<tr>"
+					 (list (list "<td>([0-9][0-9])/([0-9][0-9])/([0-9][0-9])</td>"
+						     1 2 3)
+					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
+					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
+					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1))
+					 result))))
+	    (or (and (every (lambda (row)
+                              (and (or (equal? (second row) (fourth row))
+                                       (begin (format "Second Element ~g != fourth element ~g~%" (second row) (fourth row)) #f))
+                                   (or (= 0 (string->number (car (third row))))
+                                       (begin (format "third row element ~a not 0~%" (car (third row))) #f))))
+				tbl)
+                     (or (= 0 (tbl-ref->number tbl 0 1))
+                         (begin (format #t "Item 1 failed: ~g not 0~%" (tbl-ref->number tbl 0 1)) #f))
+                     (or (= 1 (tbl-ref->number tbl 1 1))
+                         (begin (format #t "Item 1 failed: ~g not 1~%" (tbl-ref->number tbl 1 1)) #f))
+                     (or (= 6 (tbl-ref->number tbl 2 1))
+                         (begin (format #t "Item 2 failed: ~g not 6~%" (tbl-ref->number tbl 2 1)) #f))
+                     (or (= 3 (tbl-row-count tbl))
+                         (begin (format #t "Item 3 failed: ~g not 3~%" (tbl-row-count tbl)) #f))
+                     (or (= 4 (tbl-column-count tbl))
+                         (begin (format #t "Item 4 failed: ~g not 4~%" (tbl-column-count tbl)) #f)))
+                (begin (format #t "Two-txn test ~a failed~%" uuid) #f))
+                ))))))
+
+
+(define (two-txn-test-2 uuid)
+  (let* ((template (gnc:find-report-template uuid))
+	 (options (gnc:make-report-options uuid))
+	 (report (constructor uuid "bar" options
+				     #t #t #f #f ""))
+	 (renderer (gnc:report-template-renderer template)))
+    (let* ((env (create-test-env))
+	   (my-asset-account (env-create-root-account env ACCT-TYPE-ASSET
+						      (gnc-default-report-currency)))
+	   (my-liability-account (env-create-root-account env ACCT-TYPE-LIABILITY
+						      (gnc-default-report-currency)))
+	   (my-expense-account (env-create-root-account env ACCT-TYPE-EXPENSE
+							(gnc-default-report-currency)))
+	   (my-income-account (env-create-root-account env ACCT-TYPE-INCOME
+						       (gnc-default-report-currency)))
+	   (date-0 (gnc:get-start-this-month))
+	   (date-1 (gnc:time64-next-day date-0))
+	   (date-2 (gnc:time64-next-day date-1)))
+      (env-create-transaction env date-1 my-income-account my-asset-account -1/1)
+      (env-create-transaction env date-1 my-expense-account my-liability-account -1/1)
+      (env-create-transaction env date-2 my-income-account my-asset-account -5/1)
+      (env-create-transaction env date-2 my-expense-account my-liability-account -5/1)
+      (begin
+	(set-option report gnc:pagename-display "Show table" #t)
+	(set-option report gnc:pagename-general "Start Date" (cons 'absolute date-0))
+	(set-option report gnc:pagename-general "End Date" (cons 'absolute date-2))
+	(set-option report gnc:pagename-general "Step Size" 'DayDelta)
+	(set-option report gnc:pagename-general "Price Source" 'pricedb-nearest)
+	(set-option report gnc:pagename-general "Report's currency"  (gnc-default-report-currency))
+	(set-option report gnc:pagename-accounts "Accounts" (list my-asset-account my-liability-account))
+
+	(let ((doc (renderer report)))
+	  (gnc:html-document-set-style-sheet! doc
+					      (gnc:report-stylesheet report))
+	  (let* ((result (gnc:html-document-render doc #f))
+		 (tbl (stream->list
+		       (pattern-streamer "<tr>"
+					 (list (list "<td>([0-9][0-9])/([0-9][0-9])/([0-9][0-9])</td>"
+						     1 2 3)
+					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
+					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
+					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1))
+					 result))))
+	    (or (and (every (lambda (row)
+				  (and (= (string->number (car (fourth row)))
+					  (+ (string->number (car (second row)))
+					     (string->number (car (third row)))))
+				       ;; txns added in pairs, so assets = liability
+				       (equal? (second row) (third row))))
+				tbl)
+			 (= 0 (tbl-ref->number tbl 0 1))
+			 (= 1 (tbl-ref->number tbl 1 1))
+			 (= 6 (tbl-ref->number tbl 2 1))
+			 (= 3 (tbl-row-count tbl))
+			 (= 4 (tbl-column-count tbl)))
+                (begin (format #t "two-txn test 2 ~a failed~%" uuid) #f))
+                ))))))
+
+(define (two-txn-test-income uuid)
+  (let* ((template (gnc:find-report-template uuid))
+	 (options (gnc:make-report-options uuid))
+	 (report (constructor uuid "bar" options
+				     #t #t #f #f ""))
+	 (renderer (gnc:report-template-renderer template)))
+    (let* ((env (create-test-env))
+	   (my-asset-account (env-create-root-account env ACCT-TYPE-ASSET
+						      (gnc-default-report-currency)))
+	   (my-liability-account (env-create-root-account env ACCT-TYPE-LIABILITY
+						      (gnc-default-report-currency)))
+	   (my-expense-account (env-create-root-account env ACCT-TYPE-EXPENSE
+							(gnc-default-report-currency)))
+	   (my-income-account (env-create-root-account env ACCT-TYPE-INCOME
+						       (gnc-default-report-currency)))
+	   (date-0 (gnc:get-start-this-month))
+	   (date-1 (gnc:time64-next-day date-0))
+	   (date-2 (gnc:time64-next-day date-1)))
+      (env-create-transaction env date-1 my-income-account my-asset-account -1/1)
+      (env-create-transaction env date-1 my-expense-account my-liability-account -1/1)
+      (env-create-transaction env date-2 my-income-account my-asset-account -5/1)
+      (env-create-transaction env date-2 my-expense-account my-liability-account -5/1)
+      (begin
+	(set-option report gnc:pagename-display "Show table" #t)
+	(set-option report gnc:pagename-general "Start Date" (cons 'absolute date-0))
+	(set-option report gnc:pagename-general "End Date" (cons 'absolute date-2))
+	(set-option report gnc:pagename-general "Step Size" 'DayDelta)
+	(set-option report gnc:pagename-general "Price Source" 'pricedb-nearest)
+	(set-option report gnc:pagename-general "Report's currency"  (gnc-default-report-currency))
+	(set-option report gnc:pagename-accounts "Accounts" (list my-income-account my-expense-account))
+
+	(let ((doc (renderer report)))
+	  (gnc:html-document-set-style-sheet! doc
+					      (gnc:report-stylesheet report))
+	  (let* ((result (gnc:html-document-render doc #f))
+		 (tbl (stream->list
+		       (pattern-streamer "<tr>"
+					 (list (list "<td>([0-9][0-9])/([0-9][0-9])/([0-9][0-9])</td>"
+						     1 2 3)
+					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
+					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
+					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1))
+					 result))))
+	    (or (and (every (lambda (row)
+				  (and (= (string->number (car (fourth row)))
+					  (+ (string->number (car (second row)))
+					     (string->number (car (third row)))))
+				       ;; txns added in pairs, so assets = liability
+				       (equal? (second row) (third row))))
+				tbl)
+			 (= 0 (tbl-ref->number tbl 0 1))
+			 (= 1 (tbl-ref->number tbl 1 1))
+			 (= 5 (tbl-ref->number tbl 2 1))
+			 (= 3 (tbl-row-count tbl))
+			 (= 4 (tbl-column-count tbl)))
+                (begin (format #t "two-txn-income test ~a failed~%" uuid) #f))
+                ))))))
+
+
+(define (closing-test uuid)
+  (let* ((template (gnc:find-report-template uuid))
+	 (options (gnc:make-report-options uuid))
+	 (report (constructor uuid "bar" options
+			      #t #t #f #f ""))
+	 (renderer (gnc:report-template-renderer template)))
+    (let* ((env (create-test-env))
+	   (my-asset-account (env-create-root-account env ACCT-TYPE-ASSET
+						      (gnc-default-report-currency)))
+	   (my-liability-account (env-create-root-account env ACCT-TYPE-LIABILITY
+						      (gnc-default-report-currency)))
+	   (my-expense-account (env-create-root-account env ACCT-TYPE-EXPENSE
+							(gnc-default-report-currency)))
+	   (my-income-account (env-create-root-account env ACCT-TYPE-INCOME
+						       (gnc-default-report-currency)))
+	   (my-equity-account (env-create-root-account env ACCT-TYPE-EQUITY
+						       (gnc-default-report-currency)))
+	   (date-0 (gnc:get-start-this-month))
+	   (date-1 (gnc:time64-next-day date-0))
+	   (date-2 (gnc:time64-next-day date-1))
+	   (date-3 (gnc:time64-next-day date-2)))
+
+      (env-create-transaction env date-1 my-income-account my-asset-account -1/1)
+      (env-create-transaction env date-2 my-income-account my-asset-account -2/1)
+      (env-create-transaction env date-3 my-income-account my-asset-account -3/1)
+
+      (let ((closing-txn (env-create-transaction env date-2 my-asset-account my-equity-account
+						 300/1)))
+	(xaccTransSetIsClosingTxn closing-txn #t))
+
+      (begin
+	(set-option report gnc:pagename-display "Show table" #t)
+	(set-option report gnc:pagename-general "Start Date" (cons 'absolute date-0))
+	(set-option report gnc:pagename-general "End Date" (cons 'absolute date-3))
+	(set-option report gnc:pagename-general "Step Size" 'DayDelta)
+	(set-option report gnc:pagename-general "Price Source" 'pricedb-nearest)
+	(set-option report gnc:pagename-general "Report's currency"  (gnc-default-report-currency))
+	(set-option report gnc:pagename-accounts "Accounts" (list my-income-account my-expense-account))
+
+	(let ((doc (renderer report)))
+	  (gnc:html-document-set-style-sheet! doc
+					      (gnc:report-stylesheet report))
+	  (let* ((result (gnc:html-document-render doc #f))
+		 (tbl (stream->list
+		       (pattern-streamer "<tr>"
+					 (list (list "<td>([0-9][0-9])/([0-9][0-9])/([0-9][0-9])</td>"
+						     1 2 3)
+					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
+					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
+					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1))
+					 result))))
+	    (or (and (every (lambda (row)
+				  (and (= (string->number (car (fourth row)))
+					  (+ (string->number (car (second row)))
+					     (string->number (car (third row)))))))
+				tbl)
+			 (= 0 (tbl-ref->number tbl 0 1))
+			 (= 1 (tbl-ref->number tbl 1 1))
+			 (= 2 (tbl-ref->number tbl 2 1))
+			 (= 3 (tbl-ref->number tbl 3 1))
+			 (= 4 (tbl-row-count tbl))
+			 (= 4 (tbl-column-count tbl)))
+                (begin (format #t "Closing-txn test ~a failed~%" uuid) #f))
+                ))))))
+
diff --git a/gnucash/report/standard-reports/test/test-standard-net-linechart.scm b/gnucash/report/standard-reports/test/test-standard-net-linechart.scm
index 1270c69..9adab00 100644
--- a/gnucash/report/standard-reports/test/test-standard-net-linechart.scm
+++ b/gnucash/report/standard-reports/test/test-standard-net-linechart.scm
@@ -18,24 +18,17 @@
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 (debug-set! stack 50000)
-;(use-modules (gnucash report new-reports reports-2))
 
 (use-modules (gnucash gnc-module))
-
-;; Guile 2 needs to load external modules at compile time
-;; otherwise the N_ syntax-rule won't be found at compile time
-;; causing the test to fail
-;; That's what the wrapper below is meant for:
-(define-syntax-rule (begin-for-syntax form ...)
-      (eval-when (load compile eval expand) (begin form ...)))
-
-(begin-for-syntax (gnc:module-load "gnucash/report/report-system" 0))
+(gnc:module-begin-syntax (gnc:module-load "gnucash/report/report-system" 0))
 (use-modules (gnucash engine))
 (use-modules (sw_engine))
-
+(use-modules (ice-9 format))
+(use-modules (ice-9 streams))
+(use-modules (srfi srfi-1))
+(use-modules (gnucash report stylesheets))
+(use-modules (gnucash engine test test-extras))
 (use-modules (gnucash report report-system test test-extras))
-
-(use-modules (gnucash report standard-reports test test-generic-net-linechart))
 (use-modules (gnucash report standard-reports net-charts))
 
 ;; Explicitly set locale to make the report output predictable
@@ -44,3 +37,194 @@
 (define (run-test)
   (run-net-asset-test net-worth-linechart-uuid))
 
+(define (set-option report page tag value)
+  ((gnc:option-setter (gnc:lookup-option (gnc:report-options report)
+					 page tag)) value))
+
+
+(define constructor (record-constructor <report>))
+
+(define (run-net-asset-test asset-report-uuid)
+  (and (two-txn-test asset-report-uuid)
+       (two-txn-test-2 asset-report-uuid)
+
+       (null-test asset-report-uuid)
+       (single-txn-test asset-report-uuid)))
+
+;; Just prove that the report exists.
+(define (null-test uuid)
+  (let* ((template (gnc:find-report-template uuid))
+	 (options (gnc:make-report-options uuid))
+	 (report (constructor uuid "bar" options
+				     #t #t #f #f ""))
+	 (renderer (gnc:report-template-renderer template)))
+
+    (let ((doc (renderer report)))
+      (gnc:html-document-set-style-sheet! doc
+					  (gnc:report-stylesheet report))
+      ;;(format #t "render: ~a\n" (gnc:html-document-render doc #f))
+      #t
+      )))
+
+(define (single-txn-test uuid)
+  (let* ((template (gnc:find-report-template uuid))
+	 (options (gnc:make-report-options uuid))
+	 (report (constructor uuid "bar" options
+				     #t #t #f #f ""))
+	 (renderer (gnc:report-template-renderer template)))
+    (let* ((env (create-test-env))
+	   (my-asset-account (env-create-root-account env ACCT-TYPE-ASSET
+						      (gnc-default-report-currency)))
+	   (my-expense-account (env-create-root-account env ACCT-TYPE-EXPENSE
+							(gnc-default-report-currency)))
+	   (my-income-account (env-create-root-account env ACCT-TYPE-INCOME
+						       (gnc-default-report-currency))))
+      (env-create-transaction env
+			       (gnc:get-start-this-month)
+			       my-income-account
+			       my-asset-account
+			       -1/1)
+      (begin
+	(set-option report gnc:pagename-display "Show table" #t)
+	(set-option report gnc:pagename-general "Start Date"
+		    (cons 'absolute (gnc:get-start-this-month)))
+	(set-option report gnc:pagename-general "End Date"
+		    (cons 'absolute (gnc:get-start-this-month)))
+	(set-option report gnc:pagename-general "Step Size" 'DayDelta)
+	(set-option report gnc:pagename-general "Price Source" 'pricedb-nearest)
+	(set-option report gnc:pagename-general "Report's currency"  (gnc-default-report-currency))
+	(set-option report gnc:pagename-accounts "Accounts" (list my-asset-account))
+
+	(let ((doc (renderer report)))
+	  (gnc:html-document-set-style-sheet! doc
+					      (gnc:report-stylesheet report))
+	  (let* ((result (gnc:html-document-render doc #f))
+		 (tbl (stream->list
+		       (pattern-streamer "<tr>"
+					 (list (list "<td>([0-9][0-9])/([0-9][0-9])/([0-9][0-9])</td>"
+						     1 2 3)
+					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
+					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
+					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1))
+					 result))))
+	    (and (= 1 (tbl-ref->number tbl 0 1))
+			 (= 0 (tbl-ref->number tbl 0 2))
+			 (= 1 (tbl-ref->number tbl 0 3))
+			 (= 1 (tbl-row-count tbl))
+			 (= 4 (tbl-column-count tbl)))))))))
+
+
+(define (two-txn-test uuid)
+  (let* ((template (gnc:find-report-template uuid))
+	 (options (gnc:make-report-options uuid))
+	 (report (constructor uuid "bar" options
+				     #t #t #f #f ""))
+	 (renderer (gnc:report-template-renderer template)))
+    (let* ((env (create-test-env))
+	   (my-asset-account (env-create-root-account env ACCT-TYPE-ASSET
+						      (gnc-default-report-currency)))
+	   (my-expense-account (env-create-root-account env ACCT-TYPE-EXPENSE
+							(gnc-default-report-currency)))
+	   (my-income-account (env-create-root-account env ACCT-TYPE-INCOME
+						       (gnc-default-report-currency)))
+	   (date-0 (gnc:get-start-this-month))
+	   (date-1 (gnc:time64-next-day date-0))
+	   (date-2 (gnc:time64-next-day date-1)))
+      (env-create-transaction env
+			       date-1
+			       my-income-account
+			       my-asset-account
+			       -1/1)
+      (env-create-transaction env
+			       date-2
+			       my-income-account
+			       my-asset-account
+			       -5/1)
+      (begin
+	(set-option report gnc:pagename-display "Show table" #t)
+	(set-option report gnc:pagename-general "Start Date" (cons 'absolute date-0))
+	(set-option report gnc:pagename-general "End Date" (cons 'absolute date-2))
+	(set-option report gnc:pagename-general "Step Size" 'DayDelta)
+	(set-option report gnc:pagename-general "Price Source" 'pricedb-nearest)
+	(set-option report gnc:pagename-general "Report's currency"  (gnc-default-report-currency))
+	(set-option report gnc:pagename-accounts "Accounts" (list my-asset-account))
+
+	(let ((doc (renderer report)))
+	  (gnc:html-document-set-style-sheet! doc
+					      (gnc:report-stylesheet report))
+	  (let* ((result (gnc:html-document-render doc #f))
+		 (tbl (stream->list
+		       (pattern-streamer "<tr>"
+					 (list (list "<td>([0-9][0-9])/([0-9][0-9])/([0-9][0-9])</td>"
+						     1 2 3)
+					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
+					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
+					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1))
+					 result))))
+	    (and (every (lambda (row)
+				  (and (equal? (second row) (fourth row))
+				       (= 0 (string->number (car (third row))))))
+				tbl)
+			 (= 0 (tbl-ref->number tbl 0 1))
+			 (= 1 (tbl-ref->number tbl 1 1))
+			 (= 6 (tbl-ref->number tbl 2 1))
+			 (= 3 (tbl-row-count tbl))
+			 (= 4 (tbl-column-count tbl)))))))))
+
+
+(define (two-txn-test-2 uuid)
+  (let* ((template (gnc:find-report-template uuid))
+	 (options (gnc:make-report-options uuid))
+	 (report (constructor uuid "bar" options
+				     #t #t #f #f ""))
+	 (renderer (gnc:report-template-renderer template)))
+    (let* ((env (create-test-env))
+	   (my-asset-account (env-create-root-account env ACCT-TYPE-ASSET
+						      (gnc-default-report-currency)))
+	   (my-liability-account (env-create-root-account env ACCT-TYPE-LIABILITY
+						      (gnc-default-report-currency)))
+	   (my-expense-account (env-create-root-account env ACCT-TYPE-EXPENSE
+							(gnc-default-report-currency)))
+	   (my-income-account (env-create-root-account env ACCT-TYPE-INCOME
+						       (gnc-default-report-currency)))
+	   (date-0 (gnc:get-start-this-month))
+	   (date-1 (gnc:time64-next-day date-0))
+	   (date-2 (gnc:time64-next-day date-1)))
+      (env-create-transaction env date-1 my-income-account my-asset-account -1/1)
+      (env-create-transaction env date-1 my-expense-account my-liability-account -1/1)
+      (env-create-transaction env date-2 my-income-account my-asset-account -5/1)
+      (env-create-transaction env date-2 my-expense-account my-liability-account -5/1)
+      (begin
+	(set-option report gnc:pagename-display "Show table" #t)
+	(set-option report gnc:pagename-general "Start Date" (cons 'absolute date-0))
+	(set-option report gnc:pagename-general "End Date" (cons 'absolute date-2))
+	(set-option report gnc:pagename-general "Step Size" 'DayDelta)
+	(set-option report gnc:pagename-general "Price Source" 'pricedb-nearest)
+	(set-option report gnc:pagename-general "Report's currency"  (gnc-default-report-currency))
+	(set-option report gnc:pagename-accounts "Accounts" (list my-asset-account my-liability-account))
+
+	(let ((doc (renderer report)))
+	  (gnc:html-document-set-style-sheet! doc
+					      (gnc:report-stylesheet report))
+	  (let* ((result (gnc:html-document-render doc #f))
+		 (tbl (stream->list
+		       (pattern-streamer "<tr>"
+					 (list (list "<td>([0-9][0-9])/([0-9][0-9])/([0-9][0-9])</td>"
+						     1 2 3)
+					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
+					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
+					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1))
+					 result))))
+	    (and (every (lambda (row)
+				  (and (= (string->number (car (fourth row)))
+					  (+ (string->number (car (second row)))
+					     (string->number (car (third row)))))
+				       ;; txns added in pairs, so assets = liability
+				       (equal? (second row) (third row))))
+				tbl)
+			 (= 0 (tbl-ref->number tbl 0 1))
+			 (= 1 (tbl-ref->number tbl 1 1))
+			 (= 6 (tbl-ref->number tbl 2 1))
+			 (= 3 (tbl-row-count tbl))
+			 (= 4 (tbl-column-count tbl)))))))))
+

commit a78e8b1035661cc6ad5ff15d7345e8ac5c5992af
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sat Jun 16 13:06:15 2018 +0800

    [test-balance-sheet] initial commit
    
    Adds basic balance-sheet testing.
    
    This does not yet test foreign-currency conversions, nor calculation
    of unrealized gains.

diff --git a/gnucash/report/standard-reports/test/CMakeLists.txt b/gnucash/report/standard-reports/test/CMakeLists.txt
index 3193ea1..3cb8776 100644
--- a/gnucash/report/standard-reports/test/CMakeLists.txt
+++ b/gnucash/report/standard-reports/test/CMakeLists.txt
@@ -9,6 +9,7 @@ set(scm_test_standard_reports_SOURCES
 set(scm_test_with_srfi64_SOURCES
   test-charts.scm
   test-transaction.scm
+  test-balance-sheet.scm
   test-income-gst.scm
 )
 
diff --git a/gnucash/report/standard-reports/test/test-balance-sheet.scm b/gnucash/report/standard-reports/test/test-balance-sheet.scm
new file mode 100644
index 0000000..61016b1
--- /dev/null
+++ b/gnucash/report/standard-reports/test/test-balance-sheet.scm
@@ -0,0 +1,140 @@
+(use-modules (gnucash gnc-module))
+(gnc:module-begin-syntax (gnc:module-load "gnucash/app-utils" 0))
+(use-modules (gnucash engine test test-extras))
+(use-modules (gnucash report standard-reports balance-sheet))
+(use-modules (gnucash report stylesheets))
+(use-modules (gnucash report report-system))
+(use-modules (gnucash report report-system test test-extras))
+(use-modules (srfi srfi-64))
+(use-modules (gnucash engine test srfi64-extras))
+(use-modules (sxml simple))
+(use-modules (sxml xpath))
+
+;; This is implementation testing for Balance Sheet.
+
+(define balance-sheet-uuid "c4173ac99b2b448289bf4d11c731af13")
+
+;; Explicitly set locale to make the report output predictable
+(setlocale LC_ALL "C")
+
+(define (run-test)
+  (test-runner-factory gnc:test-runner)
+  (test-begin "balsheet.scm")
+  (null-test)
+  (balsheet-tests)
+  (test-end "balsheet.scm"))
+
+(define (options->sxml options test-title)
+  (gnc:options->sxml balance-sheet-uuid options "test-balsheet" test-title))
+
+(define (set-option! options section name value)
+  (let ((option (gnc:lookup-option options section name)))
+    (if option
+        (gnc:option-set-value option value)
+        (test-assert (format #f "wrong-option ~a ~a" section name) #f))))
+
+(define structure
+  (list "Root" (list (cons 'type ACCT-TYPE-ASSET))
+        (list "Asset"
+              (list "Bank"
+                    (list "Bank-Sub"))
+              (list "A/Receivable" (list (cons 'type ACCT-TYPE-RECEIVABLE))))
+        (list "Liability" (list (cons 'type ACCT-TYPE-PAYABLE))
+              (list "CreditCard")
+              (list "A/Payable"))
+        (list "Equity" (list (cons 'type ACCT-TYPE-EQUITY)))
+        (list "Income" (list (cons 'type ACCT-TYPE-INCOME)))
+        (list "Expenses" (list (cons 'type ACCT-TYPE-EXPENSE)))))
+
+(define (null-test)
+  ;; This null-test tests for the presence of report.
+  (let ((options (gnc:make-report-options balance-sheet-uuid)))
+    (test-assert "null-test" (options->sxml options "null-test"))))
+
+(define (balsheet-tests)
+  ;; This function will perform implementation testing on the transaction report.
+  (let* ((env (create-test-env))
+         (account-alist (env-create-account-structure-alist env structure))
+         (bank (cdr (assoc "Bank" account-alist)))
+         (banksub (cdr (assoc "Bank-Sub" account-alist)))
+         (income (cdr (assoc "Income" account-alist)))
+         (expense (cdr (assoc "Expenses" account-alist)))
+         (equity (cdr (assoc "Equity" account-alist)))
+         (creditcard (cdr (assoc "CreditCard" account-alist)))
+         (payable (cdr (assoc "A/Payable" account-alist)))
+         (receivable (cdr (assoc "A/Receivable" account-alist)))
+         (YEAR (gnc:time64-get-year (gnc:get-today))))
+
+    (define (default-testing-options)
+      ;; To ease testing of transaction report, we will set default
+      ;; options for generating reports. We will elable extra columns
+      ;; for Exporting, disable generation of informational text, and
+      ;; disable indenting. These options will be tested separately as
+      ;; the first test group. By default, we'll select the modern dates.
+      (let ((options (gnc:make-report-options balance-sheet-uuid)))
+        (set-option! options "General" "Balance Sheet Date" (cons 'relative 'end-cal-year))
+        options))
+
+    (define* (create-txn DD MM YY DESC list-of-splits #:optional txn-type)
+      (let ((txn (xaccMallocTransaction (gnc-get-current-book))))
+        (xaccTransBeginEdit txn)
+        (xaccTransSetDescription txn DESC)
+        (xaccTransSetCurrency txn (gnc-default-report-currency))
+        (xaccTransSetDate txn DD MM YY)
+        (for-each
+         (lambda (tfr)
+           (let ((split (xaccMallocSplit (gnc-get-current-book))))
+             (xaccSplitSetParent split txn)
+             (xaccSplitSetAccount split (cdr tfr))
+             (xaccSplitSetValue split (car tfr))
+             (xaccSplitSetAmount split (car tfr))))
+         list-of-splits)
+        (if txn-type
+            (xaccTransSetTxnType txn txn-type))
+        (xaccTransCommitEdit txn)
+        txn))
+
+    (create-txn 1 1 YEAR "invoice charge $100"
+                (list (cons -100 income)
+                      (cons  100 receivable))
+                TXN-TYPE-INVOICE)
+
+    (create-txn 1 2 YEAR "receive part-payment $98"
+                (list (cons -98 receivable)
+                      (cons  98 bank))
+                TXN-TYPE-PAYMENT)
+
+    (create-txn 1 3 YEAR "receive bill $55"
+                (list (cons  55 expense)
+                      (cons -55 payable))
+                TXN-TYPE-INVOICE)
+
+    (create-txn 1 4 YEAR "part-pay bill $50 using creditcard"
+                (list (cons  50 payable)
+                      (cons -50 creditcard))
+                TXN-TYPE-PAYMENT)
+
+    (create-txn 1 5 YEAR "part-pay creditcard from bank"
+                (list (cons  47 creditcard)
+                      (cons -47 banksub)))
+
+    ;; Finally we can begin testing
+    (test-begin "display options")
+
+    (let* ((options (default-testing-options))
+           (sxml (options->sxml options "default")))
+
+      (test-equal "total assets = $53.00"
+        (list "Total Assets" "$53.00")
+        (sxml->table-row-col sxml 1 7 #f))
+
+      (test-equal "total liabilities = $8.00"
+        (list "Total Liabilities" "$8.00")
+        (sxml->table-row-col sxml 1 14 #f))
+
+      (test-equal "total equity  = $45.00"
+        (list "Total Equity" "$45.00")
+        (sxml->table-row-col sxml 1 19 #f))
+
+      )
+    (test-end "display options")))

commit 6a62b8db8c20796af6e4cb27abba4b2995f3f912
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sat Jun 16 15:28:45 2018 +0800

    [balance-sheet] sanitize & in (add-subtotal-line)

diff --git a/gnucash/report/standard-reports/balance-sheet.scm b/gnucash/report/standard-reports/balance-sheet.scm
index 3f86026..d3d2b2e 100644
--- a/gnucash/report/standard-reports/balance-sheet.scm
+++ b/gnucash/report/standard-reports/balance-sheet.scm
@@ -697,7 +697,8 @@
                                   total-liabilities? liability-balance))
 
           (add-subtotal-line
-           right-table (_ "Total Liabilities & Equity")
+           right-table (gnc:html-string-sanitize
+                        (_ "Total Liabilities & Equity"))
 	   #f liability-plus-equity)
 	  
 	  (gnc:html-document-add-object!

commit 62b5c8138d1b8d316ed0bfee12f524c75d2f9630
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sat Jun 16 15:15:38 2018 +0800

    [html-acct-table] sanitize   in (gnc-commodity-table)
    
    This sanitizes the commodity-table html.
    (improves my first commit!)

diff --git a/gnucash/report/report-system/html-acct-table.scm b/gnucash/report/report-system/html-acct-table.scm
index cb8a387..c7a2173 100644
--- a/gnucash/report/report-system/html-acct-table.scm
+++ b/gnucash/report/report-system/html-acct-table.scm
@@ -1156,7 +1156,10 @@
 	   ;; add the account balance in the respective commodity
 	   (gnc:make-html-table-cell/markup
 	    "number-cell" bal)
-	   " "
+           (let ((spacer (gnc:make-html-table-cell)))
+             (gnc:html-table-cell-set-style!
+              spacer "td" 'attribute (list "style" "min-width: 1em"))
+             spacer)
 	   ;; add the account balance in the report commodity
 	   (gnc:make-html-table-cell/markup
 	    "number-cell" (exchange-fn bal report-commodity))

commit 1b4a2acb4509ba6974045865767b8a529c4a36c0
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Sat Jun 16 10:31:14 2018 +0100

    Add the ability to have register cell tooltips
    
    Add the ability to display tooltips on the register sheet based on type
    of cell.

diff --git a/gnucash/register/ledger-core/split-register-model.c b/gnucash/register/ledger-core/split-register-model.c
index bd13295..8782d3c 100644
--- a/gnucash/register/ledger-core/split-register-model.c
+++ b/gnucash/register/ledger-core/split-register-model.c
@@ -497,6 +497,17 @@ gnc_split_register_get_fcredit_label (VirtualLocation virt_loc,
     return _("Credit Formula");
 }
 
+
+static char *
+gnc_split_register_get_default_tooltip (VirtualLocation virt_loc,
+                                     gpointer user_data)
+{
+    SplitRegister *reg = user_data;
+    const char *tooltip = gnc_table_get_entry(reg->table, virt_loc);
+
+    return g_strdup (tooltip);
+}
+
 static gnc_numeric
 get_trans_total_amount (SplitRegister *reg, Transaction *trans)
 {
@@ -2343,6 +2354,7 @@ gnc_split_register_model_new (void)
 
     model = gnc_table_model_new ();
 
+    // entry handlers
     gnc_table_model_set_entry_handler (model,
                                        gnc_split_register_get_date_entry,
                                        DATE_CELL);
@@ -2443,7 +2455,7 @@ gnc_split_register_model_new (void)
                                        gnc_split_register_get_rbaln_entry,
                                        RBALN_CELL);
 
-
+    // label handlers
     gnc_table_model_set_label_handler (model,
                                        gnc_split_register_get_date_label,
                                        DATE_CELL);
@@ -2548,7 +2560,13 @@ gnc_split_register_model_new (void)
                                        gnc_split_register_get_tbalance_label,
                                        RBALN_CELL);
 
+    // tooltip handlers
+//    gnc_table_model_set_default_tooltip_handler(
+//        model, gnc_split_register_get_default_tooltip);
 
+
+
+    // help handlers
     gnc_table_model_set_default_help_handler(
         model, gnc_split_register_get_default_help);
 
@@ -2612,7 +2630,7 @@ gnc_split_register_model_new (void)
                                       gnc_split_register_get_fdebt_help,
                                       FDEBT_CELL);
 
-
+    // io flag handlers
     gnc_table_model_set_io_flags_handler(
         model, gnc_split_register_get_standard_io_flags, DATE_CELL);
 
diff --git a/gnucash/register/register-core/table-allgui.c b/gnucash/register/register-core/table-allgui.c
index 038d3b0..88d31eb 100644
--- a/gnucash/register/register-core/table-allgui.c
+++ b/gnucash/register/register-core/table-allgui.c
@@ -297,6 +297,25 @@ gnc_table_get_entry (Table *table, VirtualLocation virt_loc)
     return entry;
 }
 
+char *
+gnc_table_get_tooltip (Table *table, VirtualLocation virt_loc)
+{
+    TableGetTooltipHandler tooltip_handler;
+    BasicCell *cell;
+
+    cell = gnc_table_get_cell (table, virt_loc);
+    if (!cell || !cell->cell_name)
+        return NULL;
+
+    tooltip_handler = gnc_table_model_get_tooltip_handler (table->model,
+                         cell->cell_name);
+
+    if (!tooltip_handler)
+        return NULL;
+
+    return  tooltip_handler (virt_loc, table->model->handler_user_data);
+}
+
 CellIOFlags
 gnc_table_get_io_flags (Table *table, VirtualLocation virt_loc)
 {
diff --git a/gnucash/register/register-core/table-allgui.h b/gnucash/register/register-core/table-allgui.h
index 1487058..5c0396b 100644
--- a/gnucash/register/register-core/table-allgui.h
+++ b/gnucash/register/register-core/table-allgui.h
@@ -238,6 +238,8 @@ VirtualCell *  gnc_table_get_virtual_cell (Table *table,
 
 const char *   gnc_table_get_entry (Table *table, VirtualLocation virt_loc);
 
+char *         gnc_table_get_tooltip (Table *table, VirtualLocation virt_loc);
+
 const char *   gnc_table_get_label (Table *table, VirtualLocation virt_loc);
 
 CellIOFlags    gnc_table_get_io_flags (Table *table, VirtualLocation virt_loc);
diff --git a/gnucash/register/register-core/table-model.c b/gnucash/register/register-core/table-model.c
index a5baee2..b295263 100644
--- a/gnucash/register/register-core/table-model.c
+++ b/gnucash/register/register-core/table-model.c
@@ -133,6 +133,7 @@ gnc_table_model_new (void)
     model->entry_handlers = gnc_table_model_handler_hash_new ();
     model->label_handlers = gnc_table_model_handler_hash_new ();
     model->help_handlers = gnc_table_model_handler_hash_new ();
+    model->tooltip_handlers = gnc_table_model_handler_hash_new ();
     model->io_flags_handlers = gnc_table_model_handler_hash_new ();
     model->cell_color_handlers = gnc_table_model_handler_hash_new ();
     model->cell_border_handlers = gnc_table_model_handler_hash_new ();
@@ -158,6 +159,9 @@ gnc_table_model_destroy (TableModel *model)
     gnc_table_model_handler_hash_destroy (model->label_handlers);
     model->label_handlers = NULL;
 
+    gnc_table_model_handler_hash_destroy (model->tooltip_handlers);
+    model->tooltip_handlers = NULL;
+
     gnc_table_model_handler_hash_destroy (model->help_handlers);
     model->help_handlers = NULL;
 
@@ -262,6 +266,39 @@ gnc_table_model_get_label_handler (TableModel *model, const char * cell_name)
 }
 
 void
+gnc_table_model_set_tooltip_handler (TableModel *model,
+                                   TableGetTooltipHandler tooltip_handler,
+                                   const char * cell_name)
+{
+    g_return_if_fail (model != NULL);
+    g_return_if_fail (cell_name != NULL);
+
+    gnc_table_model_handler_hash_insert (model->tooltip_handlers,
+                                         cell_name,
+                                         tooltip_handler);
+}
+
+void
+gnc_table_model_set_default_tooltip_handler
+(TableModel *model, TableGetTooltipHandler tooltip_handler)
+{
+    g_return_if_fail (model != NULL);
+
+    gnc_table_model_handler_hash_insert (model->tooltip_handlers,
+                                         DEFAULT_HANDLER,
+                                         tooltip_handler);
+}
+
+TableGetTooltipHandler
+gnc_table_model_get_tooltip_handler (TableModel *model, const char * cell_name)
+{
+    g_return_val_if_fail (model != NULL, NULL);
+
+    return gnc_table_model_handler_hash_lookup (model->tooltip_handlers,
+            cell_name);
+}
+
+void
 gnc_table_model_set_help_handler (TableModel *model,
                                   TableGetHelpHandler help_handler,
                                   const char * cell_name)
diff --git a/gnucash/register/register-core/table-model.h b/gnucash/register/register-core/table-model.h
index 2d489a6..55aee0b 100644
--- a/gnucash/register/register-core/table-model.h
+++ b/gnucash/register/register-core/table-model.h
@@ -41,7 +41,7 @@ typedef enum
     XACC_CELL_ALLOW_SHADOW     = 1 << 1,
     XACC_CELL_ALLOW_ALL        = XACC_CELL_ALLOW_INPUT | XACC_CELL_ALLOW_SHADOW,
     XACC_CELL_ALLOW_EXACT_ONLY = 1 << 2,
-    XACC_CELL_ALLOW_ENTER	     = 1 << 3,
+    XACC_CELL_ALLOW_ENTER      = 1 << 3,
     XACC_CELL_ALLOW_READ_ONLY  = XACC_CELL_ALLOW_SHADOW | XACC_CELL_ALLOW_ENTER
 } CellIOFlags;
 
@@ -73,6 +73,9 @@ typedef const char * (*TableGetLabelHandler) (VirtualLocation virt_loc,
 typedef char * (*TableGetHelpHandler) (VirtualLocation virt_loc,
                                        gpointer user_data);
 
+typedef char * (*TableGetTooltipHandler) (VirtualLocation virt_loc,
+        gpointer user_data);
+
 typedef CellIOFlags (*TableGetCellIOFlagsHandler) (VirtualLocation virt_loc,
         gpointer user_data);
 
@@ -103,6 +106,7 @@ typedef struct
     GHashTable *entry_handlers;
     GHashTable *label_handlers;
     GHashTable *help_handlers;
+    GHashTable *tooltip_handlers;
     GHashTable *io_flags_handlers;
     GHashTable *cell_color_handlers;
     GHashTable *cell_border_handlers;
@@ -174,6 +178,17 @@ TableGetHelpHandler gnc_table_model_get_help_handler
 (TableModel *model,
  const char * cell_name);
 
+void gnc_table_model_set_tooltip_handler
+(TableModel *model,
+ TableGetTooltipHandler tooltip_handler,
+ const char * cell_name);
+void gnc_table_model_set_default_tooltip_handler
+(TableModel *model,
+ TableGetTooltipHandler tooltip_handler);
+TableGetTooltipHandler gnc_table_model_get_tooltip_handler
+(TableModel *model,
+ const char * cell_name);
+
 void gnc_table_model_set_io_flags_handler
 (TableModel *model,
  TableGetCellIOFlagsHandler io_flags_handler,
diff --git a/gnucash/register/register-gnome/gnucash-sheet.c b/gnucash/register/register-gnome/gnucash-sheet.c
index 2ba4ad6..bb32e93 100644
--- a/gnucash/register/register-gnome/gnucash-sheet.c
+++ b/gnucash/register/register-gnome/gnucash-sheet.c
@@ -2620,6 +2620,66 @@ gnucash_sheet_get_type (void)
     return gnucash_sheet_type;
 }
 
+
+static gboolean
+gnucash_sheet_tooltip (GtkWidget  *widget, gint x, gint y,
+               gboolean    keyboard_mode, GtkTooltip *tooltip,
+               gpointer    user_data)
+{
+    GnucashSheet *sheet = GNUCASH_SHEET (widget);
+    GnucashCursor *cursor = sheet->cursor;
+    Table *table = sheet->table;
+    VirtualLocation virt_loc;
+    gchar *tooltip_text;
+    gint cx, cy, cw, ch;
+    GdkRectangle rect;
+    SheetBlock *block;
+    gint bx, by;
+    gint hscroll_val, vscroll_val;
+
+    if (keyboard_mode)
+        return FALSE;
+
+    // get the scroll window values
+    hscroll_val = (gint) gtk_adjustment_get_value (sheet->hadj);
+    vscroll_val = (gint) gtk_adjustment_get_value (sheet->vadj);
+
+    if (!gnucash_sheet_find_loc_by_pixel (sheet, x + hscroll_val, y + vscroll_val, &virt_loc))
+        return FALSE;
+
+    tooltip_text = gnc_table_get_tooltip (table, virt_loc);
+
+    // if tooltip_text empty, clear tooltip and return FALSE
+    if ((tooltip_text == NULL) || (g_strcmp0 (tooltip_text,"") == 0))
+    {
+        gtk_tooltip_set_text (tooltip, NULL);
+        return FALSE;
+    }
+
+    block = gnucash_sheet_get_block (sheet, virt_loc.vcell_loc);
+    if (block == NULL)
+        return FALSE;
+
+    bx = block->origin_x;
+    by = block->origin_y;
+
+    // get the cell location and dimensions
+    gnucash_sheet_style_get_cell_pixel_rel_coords (cursor->style,
+            virt_loc.phys_row_offset, virt_loc.phys_col_offset,
+            &cx, &cy, &cw, &ch);
+
+     rect.x = cx + bx - hscroll_val;
+     rect.y = cy + by - vscroll_val;
+     rect.width = cw;
+     rect.height = ch;
+
+     gtk_tooltip_set_tip_area (tooltip, &rect);
+     gtk_tooltip_set_text (tooltip, tooltip_text);
+     g_free (tooltip_text);
+     return TRUE;
+}
+
+
 GtkWidget *
 gnucash_sheet_new (Table *table)
 {
@@ -2640,6 +2700,11 @@ gnucash_sheet_new (Table *table)
                                    g_int_equal,
                                    g_free, NULL);
 
+    /* add tooltips to sheet */
+    gtk_widget_set_has_tooltip (GTK_WIDGET(sheet), TRUE);
+    g_signal_connect(G_OBJECT(sheet), "query-tooltip",
+                     G_CALLBACK(gnucash_sheet_tooltip), NULL);
+
     gnucash_sheet_refresh_from_prefs(sheet);
 
     return GTK_WIDGET(sheet);

commit 01a426bf7972328e165556bce8d05fb13b89593c
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sat Jun 16 14:17:43 2018 +0800

    [test-transaction] upgrade to test foreign conversion too
    
    Thanks to previous commit, we can now test foreign currency conversions.

diff --git a/gnucash/report/standard-reports/test/test-transaction.scm b/gnucash/report/standard-reports/test/test-transaction.scm
index 5919ec7..b60226f 100644
--- a/gnucash/report/standard-reports/test/test-transaction.scm
+++ b/gnucash/report/standard-reports/test/test-transaction.scm
@@ -566,12 +566,6 @@
       (set-option! options "Display" "Other Account Name" #t)
       (set-option! options "Display" "Other Account Code" #t)
       (let* ((sxml (options->sxml options "dual column")))
-        ;; Note. It's difficult to test converted monetary
-        ;; amounts. Although I've set transfers from USD/GBP, the
-        ;; transfers do not update the pricedb automatically,
-        ;; therefore converted amounts are displayed as $0. We are not
-        ;; testing the pricedb so it does not seem fair to test its
-        ;; output here too.
         (test-equal "dual amount headers"
           (list "Date" "Num" "Description" "Memo/Notes" "Account" "Transfer from/to"
                 "Debit (USD)" "Credit (USD)" "Debit" "Credit")
@@ -585,12 +579,15 @@
         (test-equal "GBP original currency totals = #4"
           (list 4.0)
           (map str->num (get-row-col sxml 5 10)))
-        (test-assert "USD original currency totals = $5"
+        (test-assert "USD original currency totals = $5 (tests pricedb)"
           (equal?
            (list 5.0)
+           (map str->num (get-row-col sxml 4 8))
            (map str->num (get-row-col sxml 9 7))
            (map str->num (get-row-col sxml 9 9))))
-        )
+        (test-equal "USD grand totals are correct (tests pricedb)"
+          (list "Grand Total" "$0.00" "$5.00")
+          (get-row-col sxml 11 #f)))
 
       ;; This test group will test sign reversal strategy. We will
       ;; display all transactions in the 1969-1970 series, sorted by

commit 02bb981daa5a12557379242c59f6f217ceab9ec7
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sat Jun 16 14:18:08 2018 +0800

    [test-extras] upgrade env-transfer-foreign to update pricedb

diff --git a/libgnucash/engine/test/test-extras.scm b/libgnucash/engine/test/test-extras.scm
index 32712a6..c1d37b7 100644
--- a/libgnucash/engine/test/test-extras.scm
+++ b/libgnucash/engine/test/test-extras.scm
@@ -140,6 +140,22 @@
     ;(format #t "tx ~a\n" (map xaccSplitGetValue (list split-1 split-2)))
     txn))
 
+(define (gnc-pricedb-create currency commodity time64 value)
+  ;; I think adding pricedb for a DMY date will clobber any existing
+  ;; pricedb entry for that date.
+  (let ((price (gnc-price-create (gnc-get-current-book)))
+        (pricedb (gnc-pricedb-get-db (gnc-get-current-book))))
+    (gnc-price-begin-edit price)
+    (gnc-price-set-commodity price commodity)
+    (gnc-price-set-currency price currency)
+    (gnc-price-set-time64 price time64)
+    (gnc-price-set-source price PRICE-SOURCE-XFER-DLG-VAL)
+    (gnc-price-set-source-string price "test-price")
+    (gnc-price-set-typestr price "test")
+    (gnc-price-set-value price value)
+    (gnc-price-commit-edit price)
+    (gnc-pricedb-add-price pricedb price)))
+
 (define* (env-transfer-foreign
           env
           DD MM YY         ; day/month/year
@@ -186,6 +202,10 @@
         (begin
           (xaccSplitSetMemo split-1 memo)
           (xaccSplitSetMemo split-2 memo)))
+    (gnc-pricedb-create (xaccAccountGetCommodity debit)
+                        (xaccAccountGetCommodity credit)
+                        (gnc-dmy2time64 DD MM YY)
+                        (/ amount1 amount2))
     (xaccTransCommitEdit txn)
     txn))
 

commit f1823c525ce5b2d7fed3d6fb7859e47f03c3b1c4
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sat Jun 16 11:51:33 2018 +0800

    [webkit] eradicate css?
    
    Forgot to remove in 5093a8fb141, remove all traces off css?

diff --git a/gnucash/report/business-reports/balsheet-eg.eguile.scm b/gnucash/report/business-reports/balsheet-eg.eguile.scm
index c7a91f1..c3bcfe9 100644
--- a/gnucash/report/business-reports/balsheet-eg.eguile.scm
+++ b/gnucash/report/business-reports/balsheet-eg.eguile.scm
@@ -130,7 +130,6 @@
 <meta http-equiv="content-type" content="text-html; charset=utf-8">
 <title><?scm:d coyname ?> <?scm:d reportname ?> <?scm:d (qof-print-date opt-date) ?></title>
 
-<?scm (if css? (begin ?>
 <link rel="stylesheet" href="<?scm:d opt-css-file ?>" type="text/css">
 <!-- Note that the stylesheet file is overridden by some options, i.e.
      opt-font-family and opt-font-size                                 -->
@@ -149,7 +148,6 @@
     <?scm )) ?>
   }
 </style>
-<?scm )) ?>
 
 </head>
 <body>
diff --git a/gnucash/report/business-reports/receipt.eguile.scm b/gnucash/report/business-reports/receipt.eguile.scm
index da34d85..e98e6d4 100644
--- a/gnucash/report/business-reports/receipt.eguile.scm
+++ b/gnucash/report/business-reports/receipt.eguile.scm
@@ -108,7 +108,6 @@
 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" >
 <title><?scm:d (_ "Invoice") ?> <?scm:d invoiceid ?></title>
 
-<?scm (if css? (begin ?>
 <link rel="stylesheet" href="<?scm:d opt-css-file ?>" type="text/css">
 <!-- Note that the external stylesheet file is overridden by this following: -->
 <style type="text/css">
@@ -122,7 +121,6 @@
     <?scm:d opt-heading-font ?>
   }
 </style>
-<?scm )) ?>
 
 </head>
 <body>
diff --git a/gnucash/report/business-reports/receipt.scm b/gnucash/report/business-reports/receipt.scm
index 9fb8614..2af29f7 100644
--- a/gnucash/report/business-reports/receipt.scm
+++ b/gnucash/report/business-reports/receipt.scm
@@ -240,7 +240,6 @@
          (opt-amount-due-heading    (opt-value headingpage2 optname-amount-due))
          (opt-payment-recd-heading  (opt-value headingpage2 optname-payment-recd))
          (opt-extra-notes           (opt-value notespage    optname-extra-notes))
-         (css? #t)
          (html (eguile-file-to-string
                  opt-template-file
                  (the-environment))))
diff --git a/gnucash/report/business-reports/taxinvoice.eguile.scm b/gnucash/report/business-reports/taxinvoice.eguile.scm
index e3629c9..e6fde67 100644
--- a/gnucash/report/business-reports/taxinvoice.eguile.scm
+++ b/gnucash/report/business-reports/taxinvoice.eguile.scm
@@ -130,7 +130,6 @@
 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" >
 <title><?scm:d (_ "Invoice") ?> <?scm:d invoiceid ?></title>
 
-<?scm (if css? (begin ?>
 <link rel="stylesheet" href="<?scm:d (make-file-url opt-css-file) ?>" type="text/css">
 <!-- Note that the external stylesheet file is overridden by this following: -->
 <style type="text/css">
@@ -153,7 +152,6 @@
   }
   <?scm:d opt-extra-css ?>
 </style>
-<?scm )) ?>
 
 </head>
 <body>
diff --git a/gnucash/report/business-reports/taxinvoice.scm b/gnucash/report/business-reports/taxinvoice.scm
index fe94e64..326ce4a 100644
--- a/gnucash/report/business-reports/taxinvoice.scm
+++ b/gnucash/report/business-reports/taxinvoice.scm
@@ -306,7 +306,6 @@
          (opt-jobname-text          (opt-value headingpage2 optname-jobname-text))
          (opt-extra-css             (opt-value notespage    optname-extra-css)) 
          (opt-extra-notes           (opt-value notespage    optname-extra-notes)) 
-         (css? #t)
          (html (eguile-file-to-string
                  opt-template-file
                  (the-environment))))
diff --git a/gnucash/report/locale-specific/us/taxtxf.scm b/gnucash/report/locale-specific/us/taxtxf.scm
index f8d0818..1ec3fff 100644
--- a/gnucash/report/locale-specific/us/taxtxf.scm
+++ b/gnucash/report/locale-specific/us/taxtxf.scm
@@ -3049,8 +3049,6 @@
               #f) ;;end of if
           #f) ;;end of if
           (begin  ; else do tax report
-             (if #t                     ;does gcn-html-engine-support-css? #t!
-                 (begin ;; this is for webkit
                   (gnc:html-document-set-style!
                    doc "header-just-top"
                    'tag "th"
@@ -3092,7 +3090,6 @@
                    'tag "td"
                    'attribute (list "class" "number-cell neg")
                    'attribute (list "valign" "bottom"))
-                 ))
 
              (gnc:html-document-set-style!
               doc "just-bot"
diff --git a/gnucash/report/report-system/eguile-html-utilities.scm b/gnucash/report/report-system/eguile-html-utilities.scm
index fa89152..8a7030b 100644
--- a/gnucash/report/report-system/eguile-html-utilities.scm
+++ b/gnucash/report/report-system/eguile-html-utilities.scm
@@ -71,9 +71,7 @@
 
 (define-public (foreignstyle item) 
   ;; apply styling for amount in foreign currency
-  (if css?
-    (string-append "<span class=\"foreign\">" item "</span>"))
-    (string-append "<small><i>" item "</i></small>"))
+  (string-append "<span class=\"foreign\">" item "</span>"))
 
 ;; Convert any x into something printable as HTML
 (define-public (dump x) (escape-html (object->string x)))

commit 0461eb43c39191ee197a5d8004324f4979cb9efb
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Fri Jun 15 14:39:34 2018 +0800

    [test-charts] will now test presence of *all* standard charts
    
    And will eventually test data for all chart types too.

diff --git a/gnucash/report/standard-reports/test/test-charts.scm b/gnucash/report/standard-reports/test/test-charts.scm
index 74e8466..342ef1b 100644
--- a/gnucash/report/standard-reports/test/test-charts.scm
+++ b/gnucash/report/standard-reports/test/test-charts.scm
@@ -2,6 +2,10 @@
 (gnc:module-begin-syntax (gnc:module-load "gnucash/app-utils" 0))
 (use-modules (gnucash engine test test-extras))
 (use-modules (gnucash report standard-reports net-charts))
+(use-modules (gnucash report standard-reports account-piecharts))
+(use-modules (gnucash report standard-reports cashflow-barchart))
+(use-modules (gnucash report standard-reports daily-reports))
+(use-modules (gnucash report standard-reports price-scatter))
 (use-modules (gnucash report stylesheets))
 (use-modules (gnucash report report-system))
 (use-modules (gnucash report report-system test test-extras))
@@ -14,6 +18,19 @@
 
 (define variant-alist
   (list
+   (cons 'liability-piechart "3fe6dce77da24c66bdc8f8efdea7f9ac")
+   (cons 'stock-piechart "e9418ff64f2c11e5b61d1c7508d793ed")
+   (cons 'asset-piechart "5c7fd8a1fe9a4cd38884ff54214aa88a")
+   (cons 'expense-piechart "9bf1892805cb4336be6320fe48ce5446")
+   (cons 'income-piechart "e1bd09b8a1dd49dd85760db9d82b045c")
+   (cons 'cashflow-barchart "5426e4d987f6444387fe70880e5b28a0")
+   (cons 'category-barchart-income "44f81bee049b4b3ea908f8dac9a9474e")
+   (cons 'category-barchart-expense "b1f15b2052c149df93e698fe85a81ea6")
+   (cons 'category-barchart-asset "e9cf815f79db44bcb637d0295093ae3d")
+   (cons 'category-barchart-liability "faf410e8f8da481fbc09e4763da40bcc")
+   (cons 'daily-income "5e2d129f28d14df881c3e47e3053f604")
+   (cons 'daily-expense "dde49fed4ca940959ae7d01b72742530")
+   (cons 'price-scatterplot "1d241609fd4644caad765c95be20ff4c")
    (cons 'net-worth-barchart "cbba1696c8c24744848062c7f1cf4a72")
    (cons 'net-worth-linechart "d8b63264186b11e19038001558291366")
    (cons 'income-expense-barchart "80769921e87943adade887b9835a7685")
@@ -46,15 +63,10 @@
         (list "Asset"
               (list "Bank"))
         (list "Income" (list (cons 'type ACCT-TYPE-INCOME)))
+        (list "Liability" (list (cons 'type ACCT-TYPE-LIABILITY)))
         (list "Expenses" (list (cons 'type ACCT-TYPE-EXPENSE)))
         (list "Equity" (list (cons 'type ACCT-TYPE-EQUITY)))))
 
-(define (set-option! options section name value)
-  (let ((option (gnc:lookup-option options section name)))
-    (if option
-        (gnc:option-set-value option value)
-        (test-assert (format #f "wrong-option ~a ~a" section name) #f))))
-
 (define (null-test variant)
   ;; This null-test tests for the presence of report.
   (let* ((uuid (variant->uuid variant))
@@ -62,16 +74,17 @@
     (test-assert (format #f "null-test: ~a" variant)
       (options->render uuid options "null-test"))))
 
-
-;; the following tests are not ready yet
-;; unfortunately sxml parsing requires a very valid xhtml, which means
-;; <script>
-
 (define (net-charts-test variant)
+  (define (set-option! options section name value)
+    (let ((option (gnc:lookup-option options section name)))
+      (if option
+          (gnc:option-set-value option value)
+          (test-assert (format #f "[~a] wrong-option ~a ~a" variant section name) #f))))
   (let* ((uuid (variant->uuid variant))
          (env (create-test-env))
          (account-alist (env-create-account-structure-alist env structure))
          (bank (cdr (assoc "Bank" account-alist)))
+         (liability (cdr (assoc "Liability" account-alist)))
          (income (cdr (assoc "Income" account-alist)))
          (expense (cdr (assoc "Expenses" account-alist)))
          (equity (cdr (assoc "Equity" account-alist)))
@@ -79,22 +92,43 @@
 
     (define (default-testing-options)
       (let ((options (gnc:make-report-options (variant->uuid variant))))
-        (set-option! options "Accounts" "Accounts" (list bank))
-        (set-option! options "General" "Start Date" (cons 'relative 'start-cal-year))
-        (set-option! options "General" "End Date" (cons 'relative 'end-cal-year))
+
+        (unless (memq variant '(liability-piechart asset-piechart stock-piechart))
+          (set-option! options "General" "Start Date" '(relative . start-cal-year)))
+
+        (set-option! options "General" "End Date" '(relative . end-cal-year))
+
+        (unless (eq? variant 'price-scatterplot)
+          (set-option! options "Accounts" "Accounts" (list bank liability)))
+
         options))
 
-    (env-transfer env 01 01 YEAR bank expense       5   #:description "desc-1" #:num "trn1" #:memo "memo-3")
-    (env-transfer env 21 02 YEAR income bank       10   #:description "desc-2" #:num "trn2" #:void-reason "void" #:notes "notes3")
-    (env-transfer env 11 02 YEAR income bank       29   #:description "desc-3" #:num "trn3"
-                  #:reconcile (cons #\c (gnc-dmy2time64 01 03 YEAR)))
-    (env-transfer env 01 02 YEAR bank expense      15   #:description "desc-4" #:num "trn4" #:notes "notes2" #:memo "memo-1")
-    (env-transfer env 10 03 YEAR bank expense      10   #:description "desc-5" #:num "trn5" #:void-reason "any")
-    (env-transfer env 10 03 YEAR expense bank      11   #:description "desc-6" #:num "trn6" #:notes "notes1")
-    (env-transfer env 10 04 YEAR income bank        8   #:description "desc-7" #:num "trn7" #:notes "notes1" #:memo "memo-2"
-                  #:reconcile (cons #\y (gnc-dmy2time64 01 03 YEAR)))
+    (env-transfer env 01 01 YEAR equity bank   3)
+    (env-transfer env 11 01 YEAR bank expense  8)
+    (env-transfer env 11 02 YEAR income bank   29)
+    (env-transfer env 21 02 YEAR income bank   10 #:void-reason "void")
+    (env-transfer env 22 02 YEAR liability expense 27)
+    (env-transfer env 01 03 YEAR bank expense  15)
+    (env-transfer env 10 05 YEAR bank expense  10 #:void-reason "any")
+    (env-transfer env 10 07 YEAR expense bank  11)
+    (env-transfer env 10 09 YEAR income bank    8)
 
     (let* ((options (default-testing-options)))
       (test-assert (format #f "basic report exists: ~a" variant)
-        (options->render uuid options (format #f "net-charts-test ~a default options" variant))))))
+        (options->render uuid options (format #f "net-charts-test ~a default options" variant))))
+
+    (case variant
+      ((liability-piechart stock-piechart asset-piechart expense-piechart income-piechart)
+       'piechart-tests)
+
+      ((cashflow-barchart)
+       'cashflow-barchart-test)
+
+      ((category-barchart-income category-barchart-expense category-barchart-asset category-barchart-liability)
+       'category-barchart-tests)
+
+      ((daily-income daily-expense)
+       'daily-tests)
 
+      ((net-worth-barchart income-expense-barchart net-worth-linechart income-expense-linechart)
+       'net-charts-tests))))

commit b1a165eb3b874b5c0d2e7f8f67dba4807f923c12
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Fri Jun 15 14:58:51 2018 +0800

    [test-charts] rename test-net-charts to test-charts
    
    It is not that much more difficult to test *all* charts in the same
    test-file.

diff --git a/gnucash/report/standard-reports/test/CMakeLists.txt b/gnucash/report/standard-reports/test/CMakeLists.txt
index 9646dfa..3193ea1 100644
--- a/gnucash/report/standard-reports/test/CMakeLists.txt
+++ b/gnucash/report/standard-reports/test/CMakeLists.txt
@@ -7,7 +7,7 @@ set(scm_test_standard_reports_SOURCES
 )
 
 set(scm_test_with_srfi64_SOURCES
-  test-net-charts.scm
+  test-charts.scm
   test-transaction.scm
   test-income-gst.scm
 )
diff --git a/gnucash/report/standard-reports/test/test-net-charts.scm b/gnucash/report/standard-reports/test/test-charts.scm
similarity index 100%
rename from gnucash/report/standard-reports/test/test-net-charts.scm
rename to gnucash/report/standard-reports/test/test-charts.scm

commit 1801fed486b4c2ad4acd1fa6df714be3147c003e
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Jun 15 16:42:02 2018 -0700

    Remove emacs spoor from POTFILES.in

diff --git a/po/POTFILES.in b/po/POTFILES.in
index ede339f..b8cc820 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -347,7 +347,6 @@ gnucash/import-export/ofx/gnc-ofx-import.c
 gnucash/import-export/ofx/gnc-ofx-kvp.c
 gnucash/import-export/ofx/gnc-plugin-ofx.c
 gnucash/import-export/ofx/gschemas/org.gnucash.dialogs.import.ofx.gschema.xml.in
-gnucash/import-export/qif-imp/.#assistant-qif-import.c
 gnucash/import-export/qif-imp/assistant-qif-import.c
 gnucash/import-export/qif-imp/dialog-account-picker.c
 gnucash/import-export/qif-imp/gncmod-qif-import.c

commit 7ce8c9d3363ddb388933b8f5e41ddf8607d07f8f
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Jun 15 15:21:14 2018 -0700

    Bug 796595 - QIF Import Select Account button to add a new account...
    
    is labled gnc-account-new but should be New.
    
    Made it "New Account".

diff --git a/gnucash/gtkbuilder/dialog-account-picker.glade b/gnucash/gtkbuilder/dialog-account-picker.glade
index 0bd5aa9..6144047 100644
--- a/gnucash/gtkbuilder/dialog-account-picker.glade
+++ b/gnucash/gtkbuilder/dialog-account-picker.glade
@@ -154,7 +154,7 @@
             <property name="layout_style">end</property>
             <child>
               <object class="GtkButton" id="newbutton">
-                <property name="label">gnc-account-new</property>
+                <property name="label" translatable="yes">New Account</property>
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="can_default">True</property>

commit d6de324b3283af318e44ca16bd85db6eaf3f75b1
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Jun 15 14:22:43 2018 -0700

    Bug 795272 - QIF importer causes application crash if action is invalid.
    
    Fixes the crash and also pauses after loading if there are load errors.
    The load "protection" catches exceptions other than bad-date so that it's
    real protection.
    Check for unbalanced transactions (i.e. with only one account) and don't
    try to match if there is; report the error in the error log space in the
    assistant.
    Don't proceed to finding duplicates if the new account tree hasn't been
    created, there aren't any.

diff --git a/gnucash/import-export/qif-imp/assistant-qif-import.c b/gnucash/import-export/qif-imp/assistant-qif-import.c
index 21f3e74..1ab3788 100644
--- a/gnucash/import-export/qif-imp/assistant-qif-import.c
+++ b/gnucash/import-export/qif-imp/assistant-qif-import.c
@@ -59,6 +59,7 @@
 #include "gnc-prefs.h"
 #include "gnc-ui.h"
 #include "guile-mappings.h"
+#include <gfec.h>
 
 #include "swig-runtime.h"
 
@@ -1067,6 +1068,11 @@ gnc_ui_qif_import_commodity_update(QIFImportWindow * wind)
     }
 }
 
+static void
+_gfec_error_handler(const char *message)
+{
+    PERR("qif-import:qif-to-gnc-undo encountered an error: %s", message);
+}
 
 /****************************************************************
  * gnc_ui_qif_import_convert_undo
@@ -1082,7 +1088,7 @@ gnc_ui_qif_import_convert_undo(QIFImportWindow * wind)
     gnc_set_busy_cursor(NULL, TRUE);
 
     /* Undo the conversion. */
-    scm_call_1(undo, wind->imported_account_tree);
+    gfec_apply(undo, wind->imported_account_tree, _gfec_error_handler);
 
     /* There's no imported account tree any more. */
     scm_gc_unprotect_object(wind->imported_account_tree);
@@ -1916,6 +1922,7 @@ gnc_ui_qif_import_load_progress_start_cb(GtkButton * button,
 
                 wind->ask_date_format = TRUE;
             }
+            wind->load_stop = TRUE;
         }
         else
         {
@@ -1937,22 +1944,27 @@ gnc_ui_qif_import_load_progress_start_cb(GtkButton * button,
     gtk_widget_set_sensitive(wind->load_pause, FALSE);
     gtk_widget_set_sensitive(wind->load_start, FALSE);
 
-    if (wind->load_stop == FALSE)
-    {
-        /* The file was loaded successfully. */
-        gnc_progress_dialog_set_sub(wind->load_progress, _("Loading completed"));
-        gnc_progress_dialog_set_value(wind->load_progress, 1);
+    /* The file was loaded successfully. */
+    gnc_progress_dialog_set_sub(wind->load_progress, _("Loading completed"));
+    gnc_progress_dialog_set_value(wind->load_progress, 1);
 
-        scm_gc_unprotect_object(wind->imported_files);
-        wind->imported_files = imported_files;
-        scm_gc_protect_object(wind->imported_files);
+    scm_gc_unprotect_object(wind->imported_files);
+    wind->imported_files = imported_files;
+    scm_gc_protect_object(wind->imported_files);
 
-        gtk_widget_set_sensitive(wind->load_pause, FALSE);
-        wind->busy = FALSE;
+    gtk_widget_set_sensitive(wind->load_pause, FALSE);
+    wind->busy = FALSE;
+
+    if (wind->load_stop == FALSE)
+    {
 
         /* Auto step to next page */
         gtk_assistant_set_current_page (assistant, num + 1);
     }
+    else
+    {
+        wind->load_stop = FALSE;
+    }
 }
 
 
@@ -2934,58 +2946,59 @@ gnc_ui_qif_import_convert_progress_start_cb(GtkButton * button,
         wind->busy = FALSE;
         wind->load_stop = TRUE;
     }
+    if (wind->load_stop == FALSE)
+    {
+        /* Save the imported account tree. */
+        scm_gc_unprotect_object(wind->imported_account_tree);
+        wind->imported_account_tree = retval;
+        scm_gc_protect_object(wind->imported_account_tree);
 
-    /* Save the imported account tree. */
-    scm_gc_unprotect_object(wind->imported_account_tree);
-    wind->imported_account_tree = retval;
-    scm_gc_protect_object(wind->imported_account_tree);
-
-    /*
-     * Detect potentially duplicated transactions.
-     */
+        /*
+         * Detect potentially duplicated transactions.
+         */
 
-    /* This step will fill the remainder of the bar. */
-    gnc_progress_dialog_push(wind->convert_progress, 1);
-    retval = scm_call_3(find_duplicates,
-                        scm_c_eval_string("(gnc-get-current-root-account)"),
-                        wind->imported_account_tree, progress);
-    gnc_progress_dialog_pop(wind->convert_progress);
+        /* This step will fill the remainder of the bar. */
+        gnc_progress_dialog_push(wind->convert_progress, 1);
+        retval = scm_call_3(find_duplicates,
+                            scm_c_eval_string("(gnc-get-current-root-account)"),
+                            wind->imported_account_tree, progress);
+        gnc_progress_dialog_pop(wind->convert_progress);
 
-    /* Save the results. */
-    scm_gc_unprotect_object(wind->match_transactions);
-    wind->match_transactions = retval;
-    scm_gc_protect_object(wind->match_transactions);
+        /* Save the results. */
+        scm_gc_unprotect_object(wind->match_transactions);
+        wind->match_transactions = retval;
+        scm_gc_protect_object(wind->match_transactions);
 
-    if (retval == SCM_BOOL_T)
-    {
-        /* Canceled by the user. */
-        gtk_widget_set_sensitive(wind->convert_pause, FALSE);
-        gnc_progress_dialog_set_sub(wind->convert_progress, _("Canceling"));
-        wind->busy = FALSE;
-        wind->load_stop = TRUE;
-    }
-    else if (retval == SCM_BOOL_F)
-    {
-        /* An error occurred during duplicate checking. */
+        if (retval == SCM_BOOL_T)
+        {
+            /* Canceled by the user. */
+            gtk_widget_set_sensitive(wind->convert_pause, FALSE);
+            gnc_progress_dialog_set_sub(wind->convert_progress, _("Canceling"));
+            wind->busy = FALSE;
+            wind->load_stop = TRUE;
+        }
+        else if (retval == SCM_BOOL_F)
+        {
+            /* An error occurred during duplicate checking. */
 
-        /* Remove any converted data. */
-        gnc_progress_dialog_set_sub(wind->convert_progress, _("Cleaning up"));
-        gnc_ui_qif_import_convert_undo(wind);
+            /* Remove any converted data. */
+            gnc_progress_dialog_set_sub(wind->convert_progress, _("Cleaning up"));
+            gnc_ui_qif_import_convert_undo(wind);
 
-        /* Inform the user. */
-        gnc_progress_dialog_append_log(wind->convert_progress,
-                                       _( "A bug was detected while detecting duplicates."));
-        gnc_progress_dialog_set_sub(wind->convert_progress, _("Failed"));
-        gnc_progress_dialog_reset_value(wind->convert_progress);
-        gnc_error_dialog (GTK_WINDOW (assistant), "%s",
-                          _( "A bug was detected while detecting duplicates."));
-        /* FIXME: How should we request that the user report this problem? */
+            /* Inform the user. */
+            gnc_progress_dialog_append_log(wind->convert_progress,
+                                           _( "A bug was detected while detecting duplicates."));
+            gnc_progress_dialog_set_sub(wind->convert_progress, _("Failed"));
+            gnc_progress_dialog_reset_value(wind->convert_progress);
+            gnc_error_dialog (GTK_WINDOW (assistant), "%s",
+                              _( "A bug was detected while detecting duplicates."));
+            /* FIXME: How should we request that the user report this problem? */
 
-        gtk_widget_set_sensitive(wind->convert_pause, FALSE);
-        wind->busy = FALSE;
-        wind->load_stop = TRUE;
+            gtk_widget_set_sensitive(wind->convert_pause, FALSE);
+            wind->busy = FALSE;
+            wind->load_stop = TRUE;
+        }
     }
-
     /* Enable the Assistant Forward Button */
     gtk_assistant_set_page_complete (assistant, page, TRUE);
 
diff --git a/gnucash/import-export/qif-imp/qif-to-gnc.scm b/gnucash/import-export/qif-imp/qif-to-gnc.scm
index 5f7e3f1..f5d0794 100644
--- a/gnucash/import-export/qif-imp/qif-to-gnc.scm
+++ b/gnucash/import-export/qif-imp/qif-to-gnc.scm
@@ -255,7 +255,11 @@
                                              (length (qif-file:xtns b))))))
            (work-to-do 0)
            (work-done 0))
-
+      ;; Log any errors
+      (define (errorproc message)
+        (if (string? message)
+              (qif-import:log progress-dialog "qif-import:qif-to-gnc"
+                            message)))
       ;; This procedure handles progress reporting, pause, and cancel.
       (define (update-progress)
         (set! work-done (+ 1 work-done))
@@ -379,7 +383,7 @@
             (update-progress)
 
             (if (not (qif-xtn:mark xtn))
-                (qif-import:mark-matching-xtns xtn rest))
+                (qif-import:mark-matching-xtns xtn rest errorproc))
             (if (not (null? (cdr rest)))
                 (xloop (car rest) (cdr rest)))))
 
@@ -431,7 +435,7 @@
     (lambda ()
       (catch 'cancel
              (lambda ()
-               (catch 'bad-date private-convert (lambda (key . args) key)))
+               (catch #t private-convert (lambda (key . args) key)))
              (lambda (key . args) #t)))))
 
 
@@ -810,7 +814,7 @@
 ;;  mark them so they won't be imported.
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(define (qif-import:mark-matching-xtns xtn candidate-xtns)
+(define (qif-import:mark-matching-xtns xtn candidate-xtns errorproc)
   (let splitloop ((splits-left (qif-xtn:splits xtn)))
 
     ;; splits-left starts out as all the splits of this transaction.
@@ -822,7 +826,7 @@
                  (qif-split:category-is-account? (car splits-left)))
             (set! splits-left
                   (qif-import:mark-some-splits
-                   splits-left xtn candidate-xtns))
+                   splits-left xtn candidate-xtns errorproc))
             (set! splits-left (cdr splits-left))))
 
     (if (not (null? splits-left))
@@ -835,7 +839,7 @@
 ;; don't get imported.
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(define (qif-import:mark-some-splits splits xtn candidate-xtns)
+(define (qif-import:mark-some-splits splits xtn candidate-xtns errorproc)
   (let* ((n- (lambda (n) (gnc-numeric-neg n)))
          (nsub (lambda (a b) (gnc-numeric-sub a b 0 GNC-DENOM-LCD)))
          (n+ (lambda (a b) (gnc-numeric-add a b 0 GNC-DENOM-LCD)))
@@ -910,17 +914,20 @@
     ;; this is the grind loop.  Go over every unmarked transaction in
     ;; the candidate-xtns list.
     (let xtn-loop ((xtns candidate-xtns))
-      (if (and (not (qif-xtn:mark (car xtns)))
-               (string=? (qif-xtn:from-acct (car xtns)) far-acct-name))
-          (begin
-            (set! how
-                  (qif-import:xtn-has-matches? (car xtns) near-acct-name
-                                               date amount group-amount))
-            (if how
-                (begin
-                  (qif-import:merge-and-mark-xtns xtn same-acct-splits
-                                                  (car xtns) how)
-                  (set! done #t)))))
+      (if (not (and far-acct-name near-acct-name))
+          (if errorproc
+              (errorproc "Transaction with no or only one associated account."))
+          (if (and (not (qif-xtn:mark (car xtns))))
+              (string=? (qif-xtn:from-acct (car xtns)) far-acct-name)
+              (begin
+                (set! how
+                      (qif-import:xtn-has-matches? (car xtns) near-acct-name
+                                                   date amount group-amount))
+                (if how
+                    (begin
+                      (qif-import:merge-and-mark-xtns xtn same-acct-splits
+                                                      (car xtns) how)
+                      (set! done #t))))))
       ;; iterate with the next transaction
       (if (and (not done)
                (not (null? (cdr xtns))))

commit 1f8f681732bd52fb7d858b9eeb3269c973565712
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Jun 15 11:32:37 2018 -0700

    Remove abandoned C-Language QIF implementation.

diff --git a/gnucash/import-export/CMakeLists.txt b/gnucash/import-export/CMakeLists.txt
index 3b52ed8..cca8359 100644
--- a/gnucash/import-export/CMakeLists.txt
+++ b/gnucash/import-export/CMakeLists.txt
@@ -10,7 +10,6 @@ add_subdirectory(csv-imp)
 add_subdirectory(customer-import)
 add_subdirectory(log-replay)
 add_subdirectory(ofx)
-add_subdirectory(qif)
 add_subdirectory(qif-imp)
 
 
diff --git a/gnucash/import-export/qif/CMakeLists.txt b/gnucash/import-export/qif/CMakeLists.txt
deleted file mode 100644
index 608e14c..0000000
--- a/gnucash/import-export/qif/CMakeLists.txt
+++ /dev/null
@@ -1,43 +0,0 @@
-
-#Tests for this directory are not run.
-add_subdirectory(test)
-
-set(qif_SOURCES
-  qif-context.c
-  qif-defaults.c
-  qif-file.c
-  qif-objects.c
-  qif-parse.c
-)
-
-# Add dependency on config.h
-set_source_files_properties (${qif_SOURCES} PROPERTIES OBJECT_DEPENDS ${CONFIG_H})
-
-set(qif_noinst_HEADERS
-  qif-file.h
-  qif-defaults.h
-  qif-import-p.h
-  qif-import.h
-  qif-objects.h
-  qif-objects-p.h
-  qif-parse.h
-)
-
-add_library(gncmod-qif ${qif_noinst_HEADERS} ${qif_SOURCES})
-
-target_link_libraries(gncmod-qif gncmod-generic-import gncmod-engine ${GLIB2_LDFLAGS})
-
-target_compile_definitions(gncmod-qif PRIVATE -DG_LOG_DOMAIN=\"gnc.import.qif\")
-
-if (APPLE)
-  set_target_properties (gncmod-qif PROPERTIES INSTALL_NAME_DIR "${CMAKE_INSTALL_FULL_LIBDIR}/gnucash")
-endif()
-
-install(TARGETS gncmod-qif
-  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/gnucash
-  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/gnucash
-  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
-# No headers to install.
-
-set_local_dist(qif_DIST_local CMakeLists.txt ${qif_SOURCES} ${qif_noinst_HEADERS})
-set(qif_DIST ${qif_DIST_local} ${test_qif_DIST} PARENT_SCOPE)
diff --git a/gnucash/import-export/qif/qif-context.c b/gnucash/import-export/qif/qif-context.c
deleted file mode 100644
index 17fa472..0000000
--- a/gnucash/import-export/qif/qif-context.c
+++ /dev/null
@@ -1,417 +0,0 @@
-/*
- * qif-context.c -- create/destroy QIF Contexts
- *
- * Written By:	Derek Atkins <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <glib.h>
-
-#include "qif-import-p.h"
-#include "qif-objects-p.h"
-
-static void qif_object_map_get_helper(gpointer key, gpointer value, gpointer listp);
-
-QifContext
-qif_context_new(void)
-{
-    QifContext ctx = g_new0(struct _QifContext, 1);
-
-    ctx->object_lists = g_hash_table_new(g_str_hash, g_str_equal);
-    ctx->object_maps = g_hash_table_new(g_str_hash, g_str_equal);
-
-    return ctx;
-}
-
-void
-qif_context_destroy(QifContext ctx)
-{
-    GList *node, *temp;
-    QifContext fctx;
-
-    if (!ctx) return;
-
-    /* First, try to destroy all the children contexts */
-    for (node = ctx->files; node; node = temp)
-    {
-        fctx = node->data;
-        temp = node->next;
-        qif_context_destroy(fctx);
-    }
-
-    /* ok, at this point we're actually destroying this context. */
-
-    /* force the end of record */
-    if (ctx->handler && ctx->handler->end)
-        ctx->handler->end(ctx);
-
-    /* destroy the state objects */
-    qif_object_list_destroy(ctx);
-    qif_object_map_destroy(ctx);
-
-    /* Remove us from our parent context */
-    if (ctx->parent)
-        ctx->parent->files = g_list_remove(ctx->parent->files, ctx);
-
-    g_free(ctx->filename);
-
-    g_assert(ctx->files == NULL);
-    g_free(ctx);
-}
-
-static GList *
-qif_context_get_foo_helper(QifContext ctx, GFunc get_helper)
-{
-    GHashTable *ht;
-    GList *node, *list = NULL;
-    QifContext fctx;
-
-    g_return_val_if_fail(ctx, NULL);
-    g_return_val_if_fail(ctx->parsed, NULL);
-    g_return_val_if_fail(get_helper, NULL);
-
-    ht = g_hash_table_new(g_direct_hash, g_direct_equal);
-
-    for (node = ctx->files; node; node = node->next)
-    {
-        fctx = node->data;
-        qif_object_list_foreach(fctx, QIF_O_TXN, get_helper, ht);
-    }
-
-    g_hash_table_foreach(ht, qif_object_map_get_helper, &list);
-    g_hash_table_destroy(ht);
-
-    return list;
-}
-
-static void
-qif_get_accts_helper(gpointer obj, gpointer htp)
-{
-    QifTxn txn = obj;
-    QifSplit split;
-    GHashTable *ht = htp;
-    GList *node;
-
-    if (txn->from_acct)
-        g_hash_table_insert(ht, txn->from_acct, txn->from_acct);
-
-    /* The default_split is using the from_acct, so we can ignore it */
-
-    for (node = txn->splits; node; node = node->next)
-    {
-        split = node->data;
-        if (split->cat.obj && split->cat_is_acct)
-            g_hash_table_insert(ht, split->cat.acct, split->cat.acct);
-    }
-}
-
-GList *
-qif_context_get_accounts(QifContext ctx)
-{
-    return qif_context_get_foo_helper(ctx, qif_get_accts_helper);
-}
-
-static void
-qif_get_cats_helper(gpointer obj, gpointer htp)
-{
-    QifTxn txn = obj;
-    QifSplit split;
-    GHashTable *ht = htp;
-    GList *node;
-
-    /* default_split uses from_acct, so no categories */
-
-    for (node = txn->splits; node; node = node->next)
-    {
-        split = node->data;
-        if (split->cat.obj && !split->cat_is_acct)
-            g_hash_table_insert(ht, split->cat.cat, split->cat.cat);
-    }
-}
-
-GList *
-qif_context_get_categories(QifContext ctx)
-{
-    return qif_context_get_foo_helper(ctx, qif_get_cats_helper);
-}
-
-/*****************************************************************************/
-
-/*
- * Insert and remove a QifObject from the Object Maps in this Qif Context
- */
-
-gint
-qif_object_map_count(QifContext ctx, const char *type)
-{
-    GHashTable *ht;
-
-    g_return_val_if_fail(ctx, 0);
-    g_return_val_if_fail(ctx->object_maps, 0);
-    g_return_val_if_fail(type, 0);
-
-    ht = g_hash_table_lookup(ctx->object_maps, type);
-    if (!ht)
-        return 0;
-
-    return g_hash_table_size(ht);
-}
-
-void
-qif_object_map_foreach(QifContext ctx, const char *type, GHFunc func, gpointer arg)
-{
-    GHashTable *ht;
-
-    g_return_if_fail(ctx);
-    g_return_if_fail(ctx->object_maps);
-    g_return_if_fail(type);
-
-    ht = g_hash_table_lookup(ctx->object_maps, type);
-    if (ht)
-        g_hash_table_foreach(ht, func, arg);
-}
-
-void
-qif_object_map_insert(QifContext ctx, const char *key, QifObject obj)
-{
-    GHashTable *ht;
-
-    g_return_if_fail(ctx);
-    g_return_if_fail(ctx->object_maps);
-    g_return_if_fail(key);
-    g_return_if_fail(obj);
-    g_return_if_fail(obj->type);
-
-    ht = g_hash_table_lookup(ctx->object_maps, obj->type);
-    if (!ht)
-    {
-        ht = g_hash_table_new(g_str_hash, g_str_equal);
-        g_assert(ht);
-        g_hash_table_insert(ctx->object_maps, (gpointer)obj->type, ht);
-    }
-
-    g_hash_table_insert(ht, (gpointer)key, obj);
-}
-
-void
-qif_object_map_remove(QifContext ctx, const char *type, const char *key)
-{
-    GHashTable *ht;
-
-    g_return_if_fail(ctx);
-    g_return_if_fail(ctx->object_maps);
-    g_return_if_fail(type);
-    g_return_if_fail(key);
-
-    ht = g_hash_table_lookup(ctx->object_maps, type);
-    if (!ht) return;
-
-    g_hash_table_remove(ht, key);
-}
-
-QifObject
-qif_object_map_lookup(QifContext ctx, const char *type, const char *key)
-{
-    GHashTable *ht;
-
-    g_return_val_if_fail(ctx, NULL);
-    g_return_val_if_fail(ctx->object_maps, NULL);
-    g_return_val_if_fail(type, NULL);
-    g_return_val_if_fail(key, NULL);
-
-    ht = g_hash_table_lookup(ctx->object_maps, type);
-    if (!ht) return NULL;
-
-    return g_hash_table_lookup(ht, key);
-}
-
-/* This GList _SHOULD_ be freed by the caller */
-
-static void
-qif_object_map_get_helper(gpointer key, gpointer value, gpointer arg)
-{
-    GList **listp = arg;
-    g_return_if_fail(listp);
-
-    *listp = g_list_prepend(*listp, value);
-}
-
-GList *
-qif_object_map_get(QifContext ctx, const char *type)
-{
-    GHashTable *ht;
-    GList *list = NULL;
-
-    g_return_val_if_fail(ctx, NULL);
-    g_return_val_if_fail(ctx->object_maps, NULL);
-    g_return_val_if_fail(type, NULL);
-
-    ht = g_hash_table_lookup(ctx->object_maps, type);
-    if (!ht)
-        return NULL;
-
-    g_hash_table_foreach(ht, qif_object_map_get_helper, &list);
-
-    return list;
-}
-
-static gboolean
-qif_object_map_remove_each(gpointer key, gpointer value, gpointer arg)
-{
-    QifObject obj = value;
-    obj->destroy(obj);
-    return TRUE;
-}
-
-static gboolean
-qif_object_map_remove_all(gpointer key, gpointer value, gpointer arg)
-{
-    GHashTable *ht = value;
-
-    g_hash_table_foreach_remove(ht, qif_object_map_remove_each, NULL);
-    g_hash_table_destroy(ht);
-    return TRUE;
-}
-
-void qif_object_map_destroy(QifContext ctx)
-{
-    g_return_if_fail(ctx);
-    g_return_if_fail(ctx->object_maps);
-
-    g_hash_table_foreach_remove(ctx->object_maps, qif_object_map_remove_all, NULL);
-    g_hash_table_destroy(ctx->object_maps);
-}
-
-/*****************************************************************************/
-
-/*
- * Insert and remove a QifObject from the Object Lists in this Qif Context
- */
-
-void
-qif_object_list_reverse(QifContext ctx, const char *type)
-{
-    GList *list;
-
-    g_return_if_fail(ctx);
-    g_return_if_fail(ctx->object_lists);
-    g_return_if_fail(type);
-
-    list = qif_object_list_get(ctx, type);
-    list = g_list_reverse(list);
-    g_hash_table_insert(ctx->object_lists, (gpointer)type, list);
-}
-
-gint
-qif_object_list_count(QifContext ctx, const char *type)
-{
-    GList *list;
-
-    g_return_val_if_fail(ctx, 0);
-    g_return_val_if_fail(ctx->object_lists, 0);
-    g_return_val_if_fail(type, 0);
-
-    list = g_hash_table_lookup(ctx->object_lists, type);
-    return g_list_length(list);
-}
-
-void
-qif_object_list_foreach(QifContext ctx, const char *type, GFunc func, gpointer arg)
-{
-    GList *list;
-
-    g_return_if_fail(ctx);
-    g_return_if_fail(ctx->object_lists);
-    g_return_if_fail(type);
-
-    list = qif_object_list_get(ctx, type);
-    g_list_foreach(list, func, arg);
-}
-
-void
-qif_object_list_insert(QifContext ctx, QifObject obj)
-{
-    GList *list;
-
-    g_return_if_fail(ctx);
-    g_return_if_fail(ctx->object_lists);
-    g_return_if_fail(obj);
-    g_return_if_fail(obj->type && *obj->type);
-
-    list = g_hash_table_lookup(ctx->object_lists, obj->type);
-    list = g_list_prepend(list, obj);
-    g_hash_table_insert(ctx->object_lists, (gpointer)obj->type, list);
-}
-
-void
-qif_object_list_remove(QifContext ctx, QifObject obj)
-{
-    GList *list;
-
-    g_return_if_fail(ctx);
-    g_return_if_fail(ctx->object_lists);
-    g_return_if_fail(obj);
-    g_return_if_fail(obj->type && *obj->type);
-
-    list = g_hash_table_lookup(ctx->object_lists, obj->type);
-    list = g_list_remove(list, obj);
-    g_hash_table_insert(ctx->object_lists, (gpointer)obj->type, list);
-}
-
-GList *
-qif_object_list_get(QifContext ctx, const char *type)
-{
-    g_return_val_if_fail(ctx, NULL);
-    g_return_val_if_fail(ctx->object_lists, NULL);
-    g_return_val_if_fail(type, NULL);
-
-    return g_hash_table_lookup(ctx->object_lists, type);
-}
-
-static gboolean
-qif_object_list_remove_all(gpointer key, gpointer value, gpointer arg)
-{
-    GList *list = value;
-    GList *node;
-    QifObject obj;
-
-    for (node = list; node; node = node->next)
-    {
-        obj = node->data;
-        obj->destroy(obj);
-    }
-
-    g_list_free(list);
-    return TRUE;
-}
-
-void
-qif_object_list_destroy(QifContext ctx)
-{
-    g_return_if_fail(ctx);
-    g_return_if_fail(ctx->object_lists);
-
-    g_hash_table_foreach_remove(ctx->object_lists, qif_object_list_remove_all, NULL);
-    g_hash_table_destroy(ctx->object_lists);
-}
diff --git a/gnucash/import-export/qif/qif-defaults.c b/gnucash/import-export/qif/qif-defaults.c
deleted file mode 100644
index e29c141..0000000
--- a/gnucash/import-export/qif/qif-defaults.c
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * qif-defaults.c -- QIF Defaults -- default accounts...
- *
- * Created by:	Derek Atkins <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <glib.h>
-#include <glib/gi18n.h>
-
-#include "gnc-helpers.h"
-#include "qif-import-p.h"
-#include "qif-objects-p.h"
-#include "qif-defaults.h"
-
-
-static GList *stock_list = NULL;
-static GList *ext_stock_list = NULL;
-static GList *income_list = NULL;
-static GList *expense_list = NULL;
-static GList *equity_list = NULL;
-
-#define RETURN_ACCT(c,n,l) { if (stock_list == NULL) acct_type_init(); \
-	return find_or_make_acct(c, n, l); \
-}
-
-static void
-acct_type_init(void)
-{
-    stock_list = qif_parse_acct_type("__stock__", -1);
-    ext_stock_list = qif_parse_acct_type("__extstock__", -1);
-    income_list = qif_parse_acct_type("__income__", -1);
-    expense_list = qif_parse_acct_type("__expense__", -1);
-    equity_list = qif_parse_acct_type("__equity__", -1);
-}
-
-QifAccount qif_default_equity_acct(QifContext ctx)
-{
-    char *name = g_strdup(_("Retained Earnings"));
-    RETURN_ACCT(ctx, name, equity_list);
-}
-
-QifAccount qif_default_margin_interest_acct(QifContext ctx)
-{
-    char *name = g_strdup_printf("%s%s%s", _("Margin Interest"),
-                                 gnc_get_account_separator_string(),
-                                 ctx->current_acct->name);
-    RETURN_ACCT(ctx, name, expense_list);
-}
-
-QifAccount qif_default_commission_acct(QifContext ctx)
-{
-    char *name = g_strdup_printf("%s%s%s", _("Commissions"),
-                                 gnc_get_account_separator_string(),
-                                 ctx->current_acct->name);
-    RETURN_ACCT(ctx, name, expense_list);
-}
-
-QifAccount qif_default_stock_acct(QifContext ctx, const char *security)
-{
-    char *name = g_strdup_printf("%s%s%s", ctx->current_acct->name,
-                                 gnc_get_account_separator_string(),
-                                 security);
-    RETURN_ACCT(ctx, name, stock_list);
-}
-
-QifAccount qif_default_cglong_acct(QifContext ctx, const char *security)
-{
-    char *name = g_strdup_printf("%s%s%s%s%s", _("Cap. gain (long)"),
-                                 gnc_get_account_separator_string(),
-                                 ctx->current_acct->name,
-                                 gnc_get_account_separator_string(),
-                                 security);
-    RETURN_ACCT(ctx, name, income_list);
-}
-
-QifAccount qif_default_cgmid_acct(QifContext ctx, const char *security)
-{
-    char *name = g_strdup_printf("%s%s%s%s%s", _("Cap. gain (mid)"),
-                                 gnc_get_account_separator_string(),
-                                 ctx->current_acct->name,
-                                 gnc_get_account_separator_string(),
-                                 security);
-    RETURN_ACCT(ctx, name, income_list);
-}
-
-QifAccount qif_default_cgshort_acct(QifContext ctx, const char *security)
-{
-    char *name = g_strdup_printf("%s%s%s%s%s", _("Cap. gain (short)"),
-                                 gnc_get_account_separator_string(),
-                                 ctx->current_acct->name,
-                                 gnc_get_account_separator_string(),
-                                 security);
-    RETURN_ACCT(ctx, name, income_list);
-}
-
-QifAccount qif_default_dividend_acct(QifContext ctx, const char *security)
-{
-    char *name = g_strdup_printf("%s%s%s%s%s", _("Dividends"),
-                                 gnc_get_account_separator_string(),
-                                 ctx->current_acct->name,
-                                 gnc_get_account_separator_string(),
-                                 security);
-    RETURN_ACCT(ctx, name, income_list);
-}
-
-QifAccount qif_default_interest_acct(QifContext ctx, const char *security)
-{
-    char *name = g_strdup_printf("%s%s%s%s%s", _("Interest"),
-                                 gnc_get_account_separator_string(),
-                                 ctx->current_acct->name,
-                                 gnc_get_account_separator_string(),
-                                 security);
-    RETURN_ACCT(ctx, name, income_list);
-}
-
-QifAccount qif_default_capital_return_acct(QifContext ctx, const char *security)
-{
-    char *name = g_strdup_printf("%s%s%s%s%s", _("Cap Return"),
-                                 gnc_get_account_separator_string(),
-                                 ctx->current_acct->name,
-                                 gnc_get_account_separator_string(),
-                                 security);
-    RETURN_ACCT(ctx, name, income_list);
-}
-
-QifAccount qif_default_equity_holding(QifContext ctx, const char *security)
-{
-    return qif_default_equity_acct(ctx);
-}
-
diff --git a/gnucash/import-export/qif/qif-defaults.h b/gnucash/import-export/qif/qif-defaults.h
deleted file mode 100644
index 30c6562..0000000
--- a/gnucash/import-export/qif/qif-defaults.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * qif-defaults.h -- QIF Defaults -- default accounts...
- *
- * Created by:	Derek Atkins <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#ifndef QIF_DEFAULTS_H
-#define QIF_DEFAULTS_H
-
-#include "qif-objects.h"
-#include "qif-import.h"
-
-QifAccount qif_default_equity_acct(QifContext ctx);
-QifAccount qif_default_equity_holding(QifContext ctx, const char *security);
-
-QifAccount qif_default_margin_interest_acct(QifContext ctx);
-QifAccount qif_default_commission_acct(QifContext ctx);
-QifAccount qif_default_stock_acct(QifContext ctx, const char *security);
-QifAccount qif_default_cglong_acct(QifContext ctx, const char *security);
-QifAccount qif_default_cgmid_acct(QifContext ctx, const char *security);
-QifAccount qif_default_cgshort_acct(QifContext ctx, const char *security);
-QifAccount qif_default_dividend_acct(QifContext ctx, const char *security);
-QifAccount qif_default_interest_acct(QifContext ctx, const char *security);
-QifAccount qif_default_capital_return_acct(QifContext ctx, const char *security);
-
-#endif /* QIF_DEFAULTS_H */
diff --git a/gnucash/import-export/qif/qif-file.c b/gnucash/import-export/qif/qif-file.c
deleted file mode 100644
index 775f0da..0000000
--- a/gnucash/import-export/qif/qif-file.c
+++ /dev/null
@@ -1,326 +0,0 @@
-/*
- * qif-file.c -- parse a QIF File into its pieces
- *
- * Written by:  Derek Atkins  <derek@@ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <glib.h>
-#include <glib/gstdio.h>
-#include <string.h>
-
-#include "gnc-engine.h"
-
-#include "qif-import-p.h"
-#include "qif-objects-p.h"
-
-static QofLogModule log_module = GNC_MOD_IMPORT;
-
-
-static QifLine
-qif_make_line(const char* buf, gint lineno)
-{
-    QifLine line;
-    g_return_val_if_fail(buf && *buf, NULL);
-
-    line = g_new0(struct _QifLine, 1);
-    line->type = *buf;
-    line->lineno = lineno;
-    line->line = g_strdup(buf + 1);
-
-    return line;
-}
-
-void
-qif_record_destroy(GList *record)
-{
-    GList *node;
-    QifLine line;
-
-    for (node = record; node; node = node->next)
-    {
-        line = node->data;
-        g_free(line->line);
-        g_free(line);
-    }
-
-    g_list_free(record);
-}
-
-/* This returns a record, which is a bunch of QifLines, ending
- * with a line with just a '^'.  If it finds a line that begins
- * with a !, then destroy the current record state, set the "found_bangtype",
- * and return NULL.
- */
-static GList *
-qif_make_record(QifContext ctx, char *buf, size_t bufsiz, gboolean *found_bangtype)
-{
-    GList *record = NULL;
-    QifLine line;
-
-    g_return_val_if_fail(ctx, NULL);
-    g_return_val_if_fail(buf, NULL);
-    g_return_val_if_fail(found_bangtype, NULL);
-
-    *found_bangtype = FALSE;
-
-    while (fgets(buf, bufsiz, ctx->fp) != NULL)
-    {
-
-        /* increment the line number */
-        ctx->lineno++;
-
-        /* strip start/end whitespace */
-        g_strstrip(buf);
-
-        /* if there is nothing left in the string, ignore it */
-        if (strlen(buf) == 0)
-            continue;
-
-        /* If this is a bangline, then set the flag, clear our state, and return NULL */
-        if (*buf == '!')
-        {
-            *found_bangtype = TRUE;
-            break;
-        }
-
-        /* See if this is an End of Record marker */
-        if (*buf == '^')
-        {
-            /* Yep.  If we've got a record then break and return ... */
-            if (record)
-                break;
-            /* ... otherwise just continue reading (i.e. ignore empty records) */
-            else
-                continue;
-        }
-
-        /* otherwise, add the line to the list */
-        line = qif_make_line(buf, ctx->lineno);
-        if (line)
-            record = g_list_prepend(record, line);
-
-        /* and continue... */
-    }
-
-    /* If we found a bangtype, destroy anything we've collected */
-    if (*found_bangtype)
-    {
-        if (record)
-            PERR("error loading file: incomplete record at line %d", ctx->lineno);
-
-        qif_record_destroy(record);
-        record = NULL;
-    }
-
-    return g_list_reverse(record);
-}
-
-/* read a qif file and parse it, line by line
- * return QIF_E_OK on success or some other QIF Error.
- */
-static QifError
-qif_read_file(QifContext ctx, FILE *f)
-{
-    char buf[BUFSIZ];
-    GList *record;
-    gboolean found_bang;
-    QifError err = QIF_E_OK;
-
-    g_return_val_if_fail(ctx, QIF_E_BADARGS);
-    g_return_val_if_fail(f, QIF_E_BADARGS);
-
-    ctx->fp = f;
-    ctx->lineno = -1;
-
-    do
-    {
-        found_bang = FALSE;
-        record = qif_make_record(ctx, buf, sizeof(buf), &found_bang);
-
-        /* If we got a record, process it */
-        if (record)
-        {
-            if (!ctx->handler || !ctx->handler->parse_record)
-            {
-                PERR("Trying to process QIF record without a handler at %d", ctx->lineno);
-            }
-            else
-            {
-                err = ctx->handler->parse_record(ctx, record);
-            }
-
-            /* Now destroy it; we don't need it anymore */
-            qif_record_destroy(record);
-        }
-
-        /* if we found a bangtype, process that */
-        if (found_bang)
-        {
-            g_assert(*buf == '!');
-
-            /* First, process the end of the last handler.  This could possibly
-             * merge items into the context or perform some other operation
-             */
-            if (ctx->handler && ctx->handler->end)
-            {
-                err = ctx->handler->end(ctx);
-                if (err != QIF_E_OK)
-                    break;
-            }
-
-            /* Now process the bangtype (stored in buf) to set the new handler */
-            qif_parse_bangtype(ctx, buf);
-        }
-
-    }
-    while ((record || found_bang) && err == QIF_E_OK);
-
-    /* Make sure to run any end processor */
-    if (err == QIF_E_OK && ctx->handler && ctx->handler->end)
-        err = ctx->handler->end(ctx);
-
-    if (err == QIF_E_OK)
-        qif_object_list_reverse(ctx, QIF_O_TXN);
-
-    return err;
-}
-
-static QifError
-qif_import_file(QifContext ctx, const char *filename)
-{
-    QifError err;
-    FILE *fp;
-
-    g_return_val_if_fail(ctx, QIF_E_BADARGS);
-    g_return_val_if_fail(filename, QIF_E_BADARGS);
-    g_return_val_if_fail(*filename, QIF_E_BADARGS);
-
-    /* Open the file */
-    fp = g_fopen(filename, "r");
-    if (fp == NULL)
-        return QIF_E_NOFILE;
-
-    ctx->filename = g_strdup(filename);
-
-    /* read the file */
-    err = qif_read_file(ctx, fp);
-
-    /* close the file */
-    fclose(fp);
-
-    return err;
-}
-
-
-QifContext
-qif_file_new(QifContext ctx, const char *filename)
-{
-    QifContext fctx;
-
-    g_return_val_if_fail(ctx, NULL);
-    g_return_val_if_fail(filename, NULL);
-
-    fctx = qif_context_new();
-
-    /* we should assume that we've got a bank account... just in case.. */
-    qif_parse_bangtype(fctx, "!type:bank");
-
-    /* Open the file */
-    if (qif_import_file(fctx, filename) != QIF_E_OK)
-    {
-        qif_context_destroy(fctx);
-        fctx = NULL;
-    }
-
-    /* Return the new context */
-    if (fctx)
-    {
-        ctx->files = g_list_prepend(ctx->files, fctx);
-        fctx->parent = ctx;
-
-        /* Make sure the file gets merged into the parent */
-        ctx->parsed = FALSE;
-    }
-
-    return fctx;
-}
-
-QifError
-qif_file_parse(QifContext ctx, gpointer ui_args)
-{
-    g_return_val_if_fail(ctx, QIF_E_BADARGS);
-    g_return_val_if_fail(!qif_file_needs_account(ctx), QIF_E_BADSTATE);
-
-    qif_parse_all(ctx, ui_args);
-    ctx->parsed = TRUE;
-
-    return QIF_E_OK;
-}
-
-gboolean
-qif_file_needs_account(QifContext ctx)
-{
-    g_return_val_if_fail(ctx, FALSE);
-
-    return ((ctx->parse_flags & QIF_F_TXN_NEEDS_ACCT) ||
-            (ctx->parse_flags & QIF_F_ITXN_NEEDS_ACCT));
-}
-
-const char *
-qif_file_filename(QifContext ctx)
-{
-    g_return_val_if_fail(ctx, NULL);
-    return ctx->filename;
-}
-
-static void
-set_txn_acct(gpointer obj, gpointer arg)
-{
-    QifTxn txn = obj;
-    QifAccount acct = arg;
-
-    if (!txn->from_acct)
-        txn->from_acct = acct;
-}
-
-void
-qif_file_set_default_account(QifContext ctx, const char *acct_name)
-{
-    QifAccount acct;
-
-    g_return_if_fail(ctx);
-    g_return_if_fail(acct_name);
-
-    if (! qif_file_needs_account(ctx)) return;
-
-    acct = find_or_make_acct(ctx, g_strdup(acct_name),
-                             qif_parse_acct_type_guess(ctx->parse_type));
-
-    qif_object_list_foreach(ctx, QIF_O_TXN, set_txn_acct, acct);
-
-    qif_clear_flag(ctx->parse_flags, QIF_F_TXN_NEEDS_ACCT);
-    qif_clear_flag(ctx->parse_flags, QIF_F_ITXN_NEEDS_ACCT);
-}
diff --git a/gnucash/import-export/qif/qif-file.h b/gnucash/import-export/qif/qif-file.h
deleted file mode 100644
index c4b85f1..0000000
--- a/gnucash/import-export/qif/qif-file.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/* qif-import-p.h -- a QIF Importer module (private headers)
- *
- * Written By:	Derek Atkins <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#ifndef QIF_FILE_H
-#define QIF_FILE_H
-
-struct _QifLine
-{
-    char		type;
-    gint		lineno;
-    char *	line;
-};
-
-void qif_record_destroy(GList *record);
-
-#endif /* QIF_FILE_H */
diff --git a/gnucash/import-export/qif/qif-import-p.h b/gnucash/import-export/qif/qif-import-p.h
deleted file mode 100644
index 96c6314..0000000
--- a/gnucash/import-export/qif/qif-import-p.h
+++ /dev/null
@@ -1,100 +0,0 @@
-/* qif-import-p.h -- a QIF Importer module (private headers)
- *
- * Written By:	Derek Atkins <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#ifndef QIF_IMPORT_P_H
-#define QIF_IMPORT_P_H
-
-#include "qif-import.h"
-#include "qif-objects.h"
-#include "qif-parse.h"
-#include "qif-file.h"
-
-#include <stdio.h>
-
-struct _QifHandler
-{
-    void		(*init)(QifContext ctx);
-    QifError	(*parse_record)(QifContext ctx, GList *record);
-    QifError	(*end)(QifContext ctx);
-};
-
-struct _QifContext
-{
-    /* The parent context */
-    QifContext	parent;
-
-    /* file information */
-    char *	filename;
-    FILE *	fp;
-    gint		lineno;
-
-    /* This describes what we are parsing right now */
-    QifType	parse_type;
-    QifHandler	handler;
-    gpointer	parse_state;
-
-    /* A bunch of flags for the current handler */
-    gint		parse_flags;
-    gboolean	parsed;
-
-    /* The current and "opening balance" account */
-    QifAccount	current_acct;
-    QifAccount	opening_bal_acct;
-
-    /* HashTable of Maps of data objects */
-    GHashTable *	object_maps;
-
-    /* HashTable of Lists of data objects */
-    GHashTable *	object_lists;
-
-    /* List of files */
-    GList *files;
-};
-
-/* Object Maps */
-gint qif_object_map_count(QifContext ctx, const char *type);
-void qif_object_map_foreach(QifContext ctx, const char *type,
-                            GHFunc func, gpointer arg);
-void qif_object_map_insert(QifContext ctx, const char *key, QifObject obj);
-void qif_object_map_remove(QifContext ctx, const char *type, const char *key);
-QifObject qif_object_map_lookup(QifContext ctx, const char *type, const char *key);
-void qif_object_map_destroy(QifContext ctx);
-/* GList _SHOULD_ be freed by the caller */
-GList * qif_object_map_get(QifContext ctx, const char *type);
-
-/* Object Lists */
-void qif_object_list_reverse(QifContext ctx, const char *type);
-gint qif_object_list_count(QifContext ctx, const char *type);
-void qif_object_list_foreach(QifContext ctx, const char *type,
-                             GFunc func, gpointer arg);
-void qif_object_list_insert(QifContext ctx, QifObject obj);
-void qif_object_list_remove(QifContext ctx, QifObject obj);
-void qif_object_list_destroy(QifContext ctx);
-/* GList should NOT be freed by the caller */
-GList *qif_object_list_get(QifContext ctx, const char *type);
-
-/* Set and clear flags in bit-flags */
-#define qif_set_flag(i,f) (i |= f)
-#define qif_clear_flag(i,f) (i &= ~f)
-
-#endif /* QIF_IMPORT_P_H */
diff --git a/gnucash/import-export/qif/qif-import.h b/gnucash/import-export/qif/qif-import.h
deleted file mode 100644
index 9602029..0000000
--- a/gnucash/import-export/qif/qif-import.h
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * qif-import.h -- a QIF Import module
- *
- * Written By:	Derek Atkins <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#ifndef QIF_IMPORT_H
-#define QIF_IMPORT_H
-
-#include <stdio.h>
-#include "qof.h"
-
-typedef enum
-{
-    QIF_TYPE_BANK = 1,
-    QIF_TYPE_CASH,
-    QIF_TYPE_CCARD,
-    QIF_TYPE_INVST,
-    QIF_TYPE_PORT,
-    QIF_TYPE_OTH_A,
-    QIF_TYPE_OTH_L,
-    QIF_TYPE_CLASS,
-    QIF_TYPE_CAT,
-    QIF_TYPE_SECURITY,
-    QIF_ACCOUNT,
-    QIF_AUTOSWITCH,
-    QIF_CLEAR_AUTOSWITCH
-} QifType;
-
-/* Make sure this patches */
-#define QIF_TYPE_MAX QIF_CLEAR_AUTOSWITCH
-
-typedef struct _QifHandler *QifHandler;
-typedef struct _QifContext *QifContext;
-typedef struct _QifLine *QifLine;
-
-/* Qif Flags */
-#define QIF_F_IGNORE_ACCOUNTS	(1 << 0)
-#define QIF_F_TXN_NEEDS_ACCT	(1 << 1)
-#define QIF_F_ITXN_NEEDS_ACCT	(1 << 2)
-
-/* Qif Reconciled Flag */
-typedef enum
-{
-    QIF_R_NO = 0,
-    QIF_R_CLEARED,
-    QIF_R_RECONCILED,
-    QIF_R_BUDGETED,
-} QifRecnFlag;
-
-/* Qif Errors */
-
-typedef enum
-{
-    QIF_E_OK = 0,
-    QIF_E_INTERNAL,
-    QIF_E_BADSTATE,
-    QIF_E_BADARGS,
-    QIF_E_NOFILE,
-} QifError;
-
-
-/* Qif (investment?) Actions */
-typedef enum
-{
-    QIF_A_NONE = 0,
-    QIF_A_BUY,
-    QIF_A_BUYX,
-    QIF_A_CGLONG,
-    QIF_A_CGLONGX,
-    QIF_A_CGMID,
-    QIF_A_CGMIDX,
-    QIF_A_CGSHORT,
-    QIF_A_CGSHORTX,
-    QIF_A_DIV,
-    QIF_A_DIVX,
-    QIF_A_EXERCISE,
-    QIF_A_EXERCISEX,
-    QIF_A_EXPIRE,
-    QIF_A_GRANT,
-    QIF_A_INTINC,
-    QIF_A_INTINCX,
-    QIF_A_MARGINT,
-    QIF_A_MARGINTX,
-    QIF_A_MISCEXP,
-    QIF_A_MISCEXPX,
-    QIF_A_MISCINC,
-    QIF_A_MISCINCX,
-    QIF_A_REINVDIV,
-    QIF_A_REINVINT,
-    QIF_A_REINVLG,
-    QIF_A_REINVMD,
-    QIF_A_REINVSG,
-    QIF_A_REINVSH,
-    QIF_A_REMINDER,
-    QIF_A_RTRNCAP,
-    QIF_A_RTRNCAPX,
-    QIF_A_SELL,
-    QIF_A_SELLX,
-    QIF_A_SHRSIN,
-    QIF_A_SHRSOUT,
-    QIF_A_STKSPLIT,
-    QIF_A_VEST,
-    QIF_A_XIN,
-    QIF_A_XOUT,
-} QifAction;
-
-/* Public API Functions */
-
-/* Create a QIF Import Context */
-QifContext qif_context_new(void);
-void qif_context_destroy(QifContext ctx);
-
-/* Open and read a QIF File.  You must pass in the parent
- * context; it will return the child (file) context
- */
-QifContext qif_file_new(QifContext ctx, const char* filename);
-
-/* Does a qif-file need a default QIF account? */
-gboolean qif_file_needs_account(QifContext ctx);
-
-/* Return the filename of the QIF file */
-const char * qif_file_filename(QifContext ctx);
-
-/* Provide a default QIF Account for the QIF File */
-void qif_file_set_default_account(QifContext ctx, const char *acct_name);
-
-/* Parse the QIF File */
-QifError qif_file_parse(QifContext ctx, gpointer ui_arg);
-
-/* Merge all the qif-files from the children and into the context */
-void qif_parse_merge_files(QifContext ctx);
-
-/* Obtain the list of USED QifAccounts and QifCategories.  Finds all
- * references from the transactions in the QifContext.  The returned
- * GList must be freed by the caller.
- */
-GList *qif_context_get_accounts(QifContext ctx);
-GList *qif_context_get_categories(QifContext ctx);
-
-#endif /* QIF_IMPORT_H */
diff --git a/gnucash/import-export/qif/qif-objects-p.h b/gnucash/import-export/qif/qif-objects-p.h
deleted file mode 100644
index dbd3aa4..0000000
--- a/gnucash/import-export/qif/qif-objects-p.h
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * qif-objects-p.h -- Private header: QIF objects for the QIF importer
- *
- * Written By:	Derek Atkins <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#ifndef QIF_OBJECTS_P_H
-#define QIF_OBJECTS_P_H
-
-#include "qof.h"
-
-#include "qif-import.h"
-#include "qif-objects.h"
-
-struct _QifAccount
-{
-    struct _QifObject obj;
-
-    char *	name;
-    char *	desc;
-
-    char *	limitstr;
-    gnc_numeric	limit;
-
-    char *	budgetstr;
-    gnc_numeric	budget;
-
-    GList *	type_list;
-
-};
-
-struct _QifCategory
-{
-    struct _QifObject obj;
-
-    char *	name;
-    char *	desc;
-    char *	taxclass;
-
-    gboolean	taxable;
-    gboolean	expense;
-    gboolean	income;
-
-    char *	budgetstr;
-    gnc_numeric	budget;
-
-};
-
-struct _QifClass
-{
-    struct _QifObject obj;
-
-    char *	name;
-    char *	desc;
-    char *	taxdesig;
-
-};
-
-struct _QifSecurity
-{
-    struct _QifObject obj;
-
-    char *	name;
-    char *	symbol;
-    char *	type;
-
-};
-
-struct _QifTxn
-{
-    struct _QifObject obj;
-
-    QifType	txn_type;
-
-    char *	datestr;
-    Timespec	date;
-
-    char *	payee;
-    char *	address;
-    char *	num;
-
-    QifRecnFlag	cleared;
-
-    /* Investment info */
-    QifInvstTxn	invst_info;
-
-    /* The default_split is the default (forward) part of the QIF transaction */
-    QifSplit	default_split;
-
-    /* The current_split (if any) defines the current "qif split" we are handling */
-    QifSplit	current_split;
-
-    /* The "from" account */
-    QifAccount	from_acct;
-
-    /* The list of splits for this txn */
-    GList *	splits;
-
-};
-
-struct _QifSplit
-{
-    char *	memo;
-
-    char *	amountstr;
-    gnc_numeric	amount;
-    gnc_numeric	value;
-
-    char *	catstr;
-
-    /* parsed category/account info */
-
-    union
-    {
-        QifObject	obj;
-        QifCategory	cat;
-        QifAccount	acct;
-    } cat;
-    gboolean	cat_is_acct;
-    QifClass	cat_class;
-
-};
-
-struct _QifInvstTxn
-{
-    QifAction	action;
-
-    gnc_numeric	amount;
-    gnc_numeric	d_amount;
-    gnc_numeric	price;
-    gnc_numeric	shares;
-    gnc_numeric	commission;
-
-    char *	amountstr;
-    char *	d_amountstr;
-    char *	pricestr;
-    char *	sharesstr;
-    char *	commissionstr;
-
-    char *	security;
-    char *	catstr;
-
-    union
-    {
-        QifObject	obj;
-        QifCategory	cat;
-        QifAccount	acct;
-    } far_cat;
-    gboolean	far_cat_is_acct;
-};
-
-/* to be run after parsing all the dates and amounts */
-void qif_txn_setup_splits(QifTxn txn);
-void qif_invst_txn_setup_splits(QifContext ctx, QifTxn txn);
-
-#endif /* QIF_OBJECTS_P_H */
diff --git a/gnucash/import-export/qif/qif-objects.c b/gnucash/import-export/qif/qif-objects.c
deleted file mode 100644
index 0fad928..0000000
--- a/gnucash/import-export/qif/qif-objects.c
+++ /dev/null
@@ -1,1468 +0,0 @@
-/*
- * qif-objects.c -- Objects for the QIF Importer
- *
- * Written by:	Derek Atkins  <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <glib.h>
-#include <string.h>
-#include "Account.h"
-
-#include "gnc-engine.h"
-
-#include "qif-import-p.h"
-#include "qif-objects-p.h"
-#include "qif-defaults.h"
-
-static QofLogModule log_module = GNC_MOD_IMPORT;
-
-/* create a new object of type t, with type-string type and
- * destroy function dest.  Requires 'obj' to be set.
- */
-#define qif_object_new(t,typ,dest) ({ \
-	obj = (QifObject) g_new0(t, 1); \
-	obj->type = typ; \
-	obj->destroy = dest; \
-	obj; \
-})
-
-/* Save the string from this "line".  Also:
- * - make sure we're not over-writing anything.
- * - make sure the 'line' object no longer references the string.
- */
-#define qif_save_str(var) { \
-	if (var) { \
-		PERR("duplicate found at line %d: %s", line->lineno, line->line); \
-		g_free(var); \
-	} \
-	(var) = line->line; \
-	line->line = NULL; \
-}
-
-/* QIF Account */
-static void
-qif_account_destroy(QifObject obj)
-{
-    QifAccount acct = (QifAccount) obj;
-
-    g_free(acct->name);
-    g_free(acct->desc);
-    g_free(acct->limitstr);
-    g_free(acct->budgetstr);
-
-    g_free(acct);
-};
-
-static QifAccount
-qif_account_new(void)
-{
-    QifObject obj;
-    QifAccount acct;
-
-    obj = qif_object_new(struct _QifAccount, QIF_O_ACCOUNT, qif_account_destroy);
-
-    acct = (QifAccount)obj;
-    acct->type_list = qif_parse_acct_type("bank", -1);
-
-    acct->limit = gnc_numeric_zero();
-    acct->budget = gnc_numeric_zero();
-    return acct;
-}
-
-/*
- * Merge acct into ctx.  If this account already exists in ctx then
- * merge in any new values from acct into the ctx version and return
- * the existing acct.  If the account does not already exist, then
- * insert it into the ctx and return it.
- */
-QifAccount
-qif_account_merge(QifContext ctx, QifAccount acct)
-{
-    QifAccount acct2 =
-        (QifAccount)qif_object_map_lookup(ctx, acct->obj.type, acct->name);
-
-    if (!acct2)
-    {
-        qif_object_map_insert(ctx, acct->obj.type, (QifObject)acct);
-        return acct;
-    }
-
-    /* obviously the name is the same, so don't worry about that */
-
-    if (!acct2->desc && acct->desc)
-        acct2->desc = g_strdup(acct->desc);
-
-    if (!acct2->type_list && acct->type_list)
-        acct2->type_list = acct->type_list;
-
-    if (!acct2->limitstr && acct->limitstr)
-    {
-        acct2->limitstr = g_strdup(acct->limitstr);
-        acct2->limit = acct->limit;
-    }
-
-    if (!acct2->budgetstr && acct->budgetstr)
-    {
-        acct2->budgetstr = g_strdup(acct->budgetstr);
-        acct2->budget = acct->budget;
-    }
-
-    return acct2;
-}
-
-static QifError
-qif_account_parse(QifContext ctx, GList *record)
-{
-    QifAccount acct, temp;
-    QifLine line;
-
-    g_return_val_if_fail(ctx, QIF_E_INTERNAL);
-    g_return_val_if_fail(record, QIF_E_BADSTATE);
-
-    acct = qif_account_new();
-
-    for (; record; record = record->next)
-    {
-        line = record->data;
-
-        switch (line->type)
-        {
-        case 'N':			/* N : account name */
-            qif_save_str(acct->name);
-            break;
-        case 'D': 			/* D : account description */
-            qif_save_str(acct->desc);
-            break;
-        case 'T':			/* T : account type */
-            acct->type_list = qif_parse_acct_type(line->line, line->lineno);
-            break;
-        case 'L':			/* L : account limit */
-            qif_save_str(acct->limitstr);
-            break;
-        case 'B':			/* B : account budget */
-            qif_save_str(acct->budgetstr);
-            break;
-        default:
-            PERR("Unknown QIF account data at line %d: %s", line->lineno, line->line);
-        }
-    }
-
-    /* Merge the account into the context */
-    temp = qif_account_merge(ctx, acct);
-    if (! (ctx->parse_flags & QIF_F_IGNORE_ACCOUNTS))
-        ctx->current_acct = temp;
-    if (temp != acct)
-        qif_account_destroy((QifObject)acct);
-
-    return QIF_E_OK;
-}
-
-/* QIF Category */
-static void
-qif_cat_destroy(QifObject obj)
-{
-    QifCategory cat = (QifCategory) obj;
-
-    g_free(cat->name);
-    g_free(cat->desc);
-    g_free(cat->taxclass);
-    g_free(cat->budgetstr);
-
-    g_free(cat);
-}
-
-static QifCategory
-qif_cat_new(void)
-{
-    QifObject obj;
-    QifCategory cat;
-
-    obj = qif_object_new(struct _QifCategory, QIF_O_CATEGORY, qif_cat_destroy);
-    cat = (QifCategory)obj;
-    cat->budget = gnc_numeric_zero();
-
-    return cat;
-}
-
-/*
- * Merge cat into ctx.  If this category already exists in ctx then
- * merge in any new values from cat into the ctx version and return
- * the existing cat.  If the category does not already exist, then
- * insert it into the ctx and return it.
- */
-QifCategory
-qif_cat_merge(QifContext ctx, QifCategory cat)
-{
-    QifCategory cat2 =
-        (QifCategory)qif_object_map_lookup(ctx, cat->obj.type, cat->name);
-
-    if (!cat2)
-    {
-        qif_object_map_insert(ctx, cat->obj.type, (QifObject)cat);
-        return cat;
-    }
-
-    /* obviously the name is the same, so don't worry about that */
-
-    if (!cat2->desc && cat->desc)
-        cat2->desc = g_strdup(cat->desc);
-
-    if (!cat2->taxclass && cat->taxclass)
-        cat2->taxclass = g_strdup(cat->taxclass);
-
-    cat2->taxable = (cat2->taxable || cat->taxable);
-    cat2->expense = (cat2->expense || cat->expense);
-    cat2->income = (cat2->income || cat->income);
-
-    if (!cat2->budgetstr && cat->budgetstr)
-    {
-        cat2->budgetstr = g_strdup(cat->budgetstr);
-        cat2->budget = cat->budget;
-    }
-
-    return cat2;
-}
-
-static QifError
-qif_cat_parse(QifContext ctx, GList *record)
-{
-    QifCategory cat;
-    QifLine line;
-
-    g_return_val_if_fail(ctx, QIF_E_INTERNAL);
-    g_return_val_if_fail(record, QIF_E_BADSTATE);
-
-    cat = qif_cat_new();
-
-    for (; record; record = record->next)
-    {
-        line = record->data;
-
-        switch (line->type)
-        {
-        case 'N':			/* N : category name */
-            qif_save_str(cat->name);
-            break;
-        case 'D':			/* D : category description */
-            qif_save_str(cat->desc);
-            break;
-        case 'T':			/* T : category is taxable? */
-            cat->taxable = TRUE;
-            break;
-        case 'E':			/* E : category is expense? */
-            cat->expense = TRUE;
-            break;
-        case 'I':			/* I : category is income? */
-            cat->income = TRUE;
-            break;
-        case 'R':			/* R : category taxclass XXX */
-            /* XXX: a number? */
-            qif_save_str(cat->taxclass);
-            break;
-        case 'B':			/* B : category budget */
-            qif_save_str(cat->budgetstr);
-            break;
-        default:
-            PERR("Unknown QIF category data at line %d: %s", line->lineno, line->line);
-        }
-    }
-
-    if (qif_cat_merge(ctx, cat) != cat)
-        qif_cat_destroy((QifObject)cat);
-
-    return QIF_E_OK;
-}
-
-/* QIF Class */
-static void
-qif_class_destroy(QifObject obj)
-{
-    QifClass qclass = (QifClass) obj;
-
-    g_free(qclass->name);
-    g_free(qclass->desc);
-    g_free(qclass->taxdesig);
-
-    g_free(qclass);
-}
-
-static QifClass
-qif_class_new()
-{
-    QifObject obj;
-
-    obj = qif_object_new(struct _QifClass, QIF_O_CLASS, qif_class_destroy);
-    return (QifClass)obj;
-}
-
-/*
- * Merge qclass into ctx.  If this class already exists in ctx then
- * merge in any new values from qclass into the ctx version and return
- * the existing qclass.  If the class does not already exist, then
- * insert it into the ctx and return it.
- */
-QifClass
-qif_class_merge(QifContext ctx, QifClass qclass)
-{
-    QifClass qclass2 =
-        (QifClass)qif_object_map_lookup(ctx, qclass->obj.type, qclass->name);
-
-    if (!qclass2)
-    {
-        qif_object_map_insert(ctx, qclass->obj.type, (QifObject)qclass);
-        return qclass;
-    }
-
-    /* obviously the name is the same, so don't worry about that */
-
-    if (!qclass2->desc && qclass->desc)
-        qclass2->desc = g_strdup(qclass->desc);
-
-    if (!qclass2->taxdesig && qclass->taxdesig)
-        qclass2->taxdesig = g_strdup(qclass->taxdesig);
-
-    return qclass2;
-}
-
-static QifError
-qif_class_parse(QifContext ctx, GList *record)
-{
-    QifClass qclass;
-    QifLine line;
-
-    g_return_val_if_fail(ctx, QIF_E_INTERNAL);
-    g_return_val_if_fail(record, QIF_E_BADSTATE);
-
-    qclass = qif_class_new();
-
-    for (; record; record = record->next)
-    {
-        line = record->data;
-
-        switch (line->type)
-        {
-        case 'N':			/* N : class name */
-            qif_save_str(qclass->name);
-            break;
-        case 'D':			/* D : class description */
-            qif_save_str(qclass->desc);
-            break;
-        case 'R':			/* R : Tax designator */
-            qif_save_str(qclass->taxdesig);
-            break;
-        default:
-            PERR("Unknown QIF class data at line %d: %s", line->lineno, line->line);
-        }
-    }
-
-    if (qif_class_merge(ctx, qclass) != qclass)
-        qif_class_destroy((QifObject)qclass);
-
-    return QIF_E_OK;
-}
-
-/* QIF Security Symbol */
-static void
-qif_security_destroy(QifObject obj)
-{
-    QifSecurity security = (QifSecurity) obj;
-
-    g_free(security->name);
-    g_free(security->symbol);
-    g_free(security->type);
-
-    g_free(security);
-}
-
-static QifSecurity
-qif_security_new()
-{
-    QifObject obj;
-
-    obj = qif_object_new(struct _QifSecurity, QIF_O_SECURITY, qif_security_destroy);
-    return (QifSecurity)obj;
-}
-
-/*
- * Merge security into ctx.  If this security already exists in ctx then
- * merge in any new values from security into the ctx version and return
- * the existing security.  If the security does not already exist, then
- * insert it into the ctx and return it.
- */
-QifSecurity
-qif_security_merge(QifContext ctx, QifSecurity security)
-{
-    QifSecurity security2 =
-        (QifSecurity)qif_object_map_lookup(ctx, security->obj.type, security->name);
-
-    if (!security2)
-    {
-        qif_object_map_insert(ctx, security->obj.type, (QifObject)security);
-        return security;
-    }
-
-    /* obviously the name is the same, so don't worry about that */
-
-    if (!security2->symbol && security->symbol)
-        security2->symbol = g_strdup(security->symbol);
-
-    if (!security2->type && security->type)
-        security2->type = g_strdup(security->type);
-
-    return security2;
-}
-
-static QifError
-qif_security_parse(QifContext ctx, GList *record)
-{
-    QifSecurity security;
-    QifLine line;
-
-    g_return_val_if_fail(ctx, QIF_E_INTERNAL);
-    g_return_val_if_fail(record, QIF_E_BADSTATE);
-
-    security = qif_security_new();
-
-    for (; record; record = record->next)
-    {
-        line = record->data;
-
-        switch (line->type)
-        {
-        case 'N':			/* N : security name */
-            qif_save_str(security->name);
-            break;
-        case 'S':			/* S : security symbol */
-            qif_save_str(security->symbol);
-            break;
-        case 'T':			/* T : security type */
-            qif_save_str(security->type);
-            break;
-        default:
-            PERR("Unknown QIF security data at line %d: %s", line->lineno, line->line);
-        }
-    }
-
-    if (qif_security_merge(ctx, security) != security)
-        qif_security_destroy((QifObject)security);
-
-    return QIF_E_OK;
-}
-
-/********************* TXN *********************/
-
-static QifSplit
-qif_split_new()
-{
-    QifSplit split = g_new0(struct _QifSplit, 1);
-
-    /* Initialize to 'zero' (even though they are not valid) */
-    split->amount = gnc_numeric_zero();
-    split->value = gnc_numeric_zero();
-
-    return split;
-}
-
-static void
-qif_split_destroy(QifSplit split)
-{
-    if (!split) return;
-
-    g_free(split->memo);
-    g_free(split->catstr);
-    g_free(split->amountstr);
-
-    g_free(split);
-}
-
-static QifSplit
-qif_split_copy(QifSplit split)
-{
-    QifSplit s = qif_split_new();
-
-    memcpy(s, split, sizeof(*s));
-    if (s->memo) s->memo = g_strdup(s->memo);
-    if (s->amountstr) s->amountstr = g_strdup(s->amountstr);
-    if (s->catstr) s->memo = g_strdup(s->catstr);
-
-    return s;
-}
-
-/* Forward declarations */
-static void qif_txn_invst_destroy(QifInvstTxn);
-
-/* QIF Transaction */
-
-static void
-qif_split_parse_category(QifContext ctx, QifSplit split)
-{
-    char *cat = NULL;
-    char *cat_class = NULL;
-    char *miscx_cat = NULL;
-    char *miscx_class = NULL;
-
-    gboolean miscx_is_acct;
-
-    static GList *types = NULL;
-
-    g_return_if_fail(ctx);
-    g_return_if_fail(split);
-    g_return_if_fail(split->cat.cat == NULL && split->cat_class == NULL);
-
-    if (qif_parse_split_category(split->catstr,
-                                 &cat, &split->cat_is_acct, &cat_class,
-                                 &miscx_cat, &miscx_is_acct, &miscx_class))
-    {
-        g_assert(cat);
-
-        if (split->cat_is_acct)
-        {
-            if (types == NULL)
-                types = qif_parse_acct_type("__any_bank__", -1);
-
-            split->cat.acct = find_or_make_acct(ctx, cat, types);
-
-        }
-        else
-            split->cat.cat = find_or_make_cat(ctx, cat);
-
-        if (cat_class)
-            split->cat_class = find_or_make_class(ctx, cat_class);
-
-        /* miscx isn't used in a normal transaction, so just ignore it */
-        if (miscx_cat)
-            g_free(miscx_cat);
-        if (miscx_class)
-            g_free(miscx_class);
-
-    }
-    else
-        PERR("Problem parsing split category: %s", split->catstr);
-}
-
-static void
-qif_txn_destroy(QifObject obj)
-{
-    QifTxn txn = (QifTxn) obj;
-    GList *node;
-    QifSplit split;
-
-    g_free(txn->datestr);
-    g_free(txn->payee);
-    g_free(txn->address);
-    g_free(txn->num);
-
-    if (txn->invst_info)
-        qif_txn_invst_destroy(txn->invst_info);
-
-    for (node = txn->splits; node; node = node->next)
-    {
-        split = node->data;
-        if (split == txn->default_split)
-            txn->default_split = NULL;
-        if (split == txn->current_split)
-            txn->current_split = NULL;
-
-        qif_split_destroy(split);
-    }
-
-    g_list_free(txn->splits);
-    qif_split_destroy(txn->default_split);
-    qif_split_destroy(txn->current_split);
-
-    g_free(txn);
-}
-
-static QifTxn
-qif_txn_new(void)
-{
-    QifObject obj;
-    QifTxn txn;
-
-    obj = qif_object_new(struct _QifTxn, "qif-txn", qif_txn_destroy);
-    txn = (QifTxn) obj;
-    txn->default_split = qif_split_new();
-
-    return txn;
-}
-
-static void
-qif_txn_init(QifContext ctx)
-{
-    qif_clear_flag(ctx->parse_flags, QIF_F_IGNORE_ACCOUNTS);
-    ctx->parse_state = NULL;
-}
-
-static gboolean
-qif_is_bad_numeric_string(const char* line)
-{
-    return (strncmp(line, "...", 3) == 0);
-}
-
-/*
- * this is called for the first transaction after each !Type: tag.
- *
- * if the first transaction after a !Type: tag has a payee of "Opening
- * Balance" or "Initial Balance", we have to massage the transaction a
- * little.  The meaning of an OB transaction is "transfer from Equity
- * to the account specified in the L line."  Idiomatically, ms-money
- * and some others use this transaction instead of an Account record
- * to specify "this" account (the from-account for all following
- * transactions), so we have to allow for that.
- *
- * Even if the payee isn't "Opening Balance", we if we have no default
- * from-account by this time we need to set one.  In that case we set
- * the default account based on the file name.
- *
- * If we DO know the account already, and this is a transfer to it,
- * it's also an opening balance regardless of the payee.
- *
- * In the end make sure that the context 'current account' is set.
- */
-static void
-qif_process_opening_balance_txn(QifContext ctx, QifTxn txn)
-{
-    QifSplit split = txn->default_split;
-    QifAccount cur_acct = NULL;	/* We know that ctx->current_acct is NULL */
-
-    g_return_if_fail(txn->invst_info == NULL);
-
-    if ((!cur_acct && txn->payee &&
-            (!strcasecmp(txn->payee, "Opening Balance") ||
-             !strcasecmp(txn->payee, "Initial Balance")) && split->cat_is_acct) ||
-            (cur_acct &&
-             ((split->cat_is_acct && !strcasecmp(split->cat.acct->name, cur_acct->name))
-              ||
-              (!split->cat_is_acct && !strcasecmp(split->cat.cat->name, cur_acct->name))))
-       )
-    {
-
-        /* This is an explicit "Opening Balance" transactions.  We need to
-         * change the "from account" to point to the equity account that
-         * the opening balance comes from...
-         */
-        if (split->cat_is_acct)
-            cur_acct = split->cat.acct;
-        else
-        {
-            g_assert(split->cat.cat);
-            cur_acct = find_or_make_acct(ctx, g_strdup(split->cat.cat->name),
-                                         qif_parse_acct_type_guess(txn->txn_type));
-            split->cat_is_acct = TRUE;
-        }
-        split->cat.acct = qif_default_equity_acct(ctx);
-    }
-
-    /*
-     * If we found an opening balance account then set up the context.
-     * If we didn't actually succeed in finding an account then
-     * set a flag so we can go back later and look for it.
-     */
-
-    if (cur_acct)
-    {
-        ctx->opening_bal_acct = cur_acct;
-        ctx->current_acct = cur_acct;
-    }
-    else
-        qif_set_flag(ctx->parse_flags, QIF_F_TXN_NEEDS_ACCT);
-}
-
-/* process all the splits in the transaction -- if this is a "split
- * transaction" then make sure the sum of all the amounts (including
- * the default split) does NOT equal zero -- if it does then we want
- * to reverse all the splits.  The "amount" should be the 'T' amount
- * from the txn.
- */
-static void
-qif_txn_fix_amounts(QifTxn txn, gnc_numeric amount)
-{
-    gnc_numeric sum = amount;
-    QifSplit split;
-    GList *node;
-
-    g_return_if_fail(txn);
-
-    /* No current_split, so this is NOT a split transaction. */
-    if (!txn->current_split) return;
-
-    /* Then add in every split in the split-list */
-    for (node = txn->splits; node; node = node->next)
-    {
-        split = node->data;
-        sum = gnc_numeric_add(sum, split->amount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
-    }
-
-    /* if the sum is not zero then reverse all the amounts in the split list */
-    if (!gnc_numeric_zero_p(sum))
-        for (node = txn->splits; node; node = node->next)
-        {
-            split = node->data;
-            split->amount = gnc_numeric_neg(split->amount);
-        }
-}
-
-static QifError
-qif_txn_parse(QifContext ctx, GList *record)
-{
-    QifTxn txn;
-    QifLine line;
-    GList *node;
-    QifSplit split;
-
-    g_return_val_if_fail(ctx, QIF_E_INTERNAL);
-    g_return_val_if_fail(record, QIF_E_BADSTATE);
-
-    txn = qif_txn_new();
-    txn->txn_type = ctx->parse_type;
-
-    for (; record; record = record->next)
-    {
-        line = record->data;
-
-        switch (line->type)
-        {
-        case 'D':			/* D : transaction date */
-            qif_save_str(txn->datestr);
-            break;
-        case 'P':			/* P : payee */
-            qif_save_str(txn->payee);
-            break;
-        case 'A':			/* A : address */
-            /* multiple 'A' lines are appended together with newlines */
-            if (txn->address)
-            {
-                char *tmp = txn->address;
-                txn->address = g_strconcat(tmp, "\n", line->line, NULL);
-                g_free(tmp);
-            }
-            else
-                qif_save_str(txn->address);
-            break;
-        case 'N':			/* N : check/transaction number */
-            qif_save_str(txn->num);
-            break;
-        case 'C':			/* C : transaction cleared flag */
-            txn->cleared = qif_parse_cleared(line);
-            break;
-        case 'L':			/* L : default split category */
-            if (!txn->current_split) qif_save_str(txn->default_split->catstr);
-            break;
-        case 'M':			/* M : default split memo */
-            if (!txn->current_split) qif_save_str(txn->default_split->memo);
-            break;
-        case 'T':			/* T : total transaction amount */
-            if (!txn->current_split && !qif_is_bad_numeric_string(line->line))
-                qif_save_str(txn->default_split->amountstr);
-            break;
-        case 'S':			/* S : split category */
-            /* This implies a quicken-style "split transaction", so we're mostly
-             * going to ignore the default_split except for internal verification.
-             */
-            txn->current_split = qif_split_new();
-            txn->splits = g_list_prepend(txn->splits, txn->current_split);
-            qif_save_str(txn->current_split->catstr);
-            break;
-        case 'E':			/* E : split memo */
-            if (txn->current_split)
-                qif_save_str(txn->current_split->memo);
-            break;
-        case '$':			/* split amount */
-            if (txn->current_split && !qif_is_bad_numeric_string(line->line))
-                qif_save_str(txn->current_split->amountstr);
-            break;
-        default:
-            PERR("Unknown QIF transaction data at line %d: %s", line->lineno, line->line);
-        }
-    }
-
-    /* If we have no date string then there is no reason to do anything else */
-    if (txn->datestr)
-    {
-        /* We delay processing the date and amount strings until later.. */
-
-        /* parse the category on each split */
-        for (node = txn->splits; node; node = node->next)
-        {
-            split = node->data;
-            if (split->catstr)
-                qif_split_parse_category(ctx, split);
-        }
-        /* ... including the default split */
-        if (txn->default_split->catstr)
-            qif_split_parse_category(ctx, txn->default_split);
-
-        /* if we don't have an account, then deal with the opening balance */
-        if (!ctx->current_acct)
-            qif_process_opening_balance_txn(ctx, txn);
-
-        /* Set the transaction's from account */
-        txn->from_acct = ctx->current_acct;
-
-        /* And add it to the process list */
-        ctx->parse_state = g_list_prepend(ctx->parse_state, txn);
-
-    }
-    else
-        /* no date?  Ignore this txn */
-        qif_txn_destroy((QifObject)txn);
-
-    return QIF_E_OK;
-}
-
-/* after we parse the amounts, fix up the transaction splits */
-void
-qif_txn_setup_splits(QifTxn txn)
-{
-    QifSplit split, this_split;
-    GList *node;
-    gnc_numeric total;
-
-    if (txn->splits)
-    {
-        /* We have a bunch of "far" splits -- maybe fix up the totals.. */
-        qif_txn_fix_amounts(txn, txn->default_split->amount);
-
-        /* Re-Compute the total for the "near" (default) split */
-        total = gnc_numeric_zero();
-        for (node = txn->splits; node; node = node->next)
-        {
-            split = node->data;
-            split->value = split->amount;
-            total = gnc_numeric_add(total, split->amount, 0, GNC_HOW_DENOM_LCD);
-        }
-
-        /* And re-set the default-split amount */
-        txn->default_split->amount = gnc_numeric_neg(total);
-
-    }
-    else
-    {
-        /* not a split txn.  Compute the "far" split by copying the "near"
-         *  split and then moving the 'near' split to the far split.
-         */
-
-        /* First make a copy of this transaction and move the copy to the 'near' */
-        split = txn->default_split;
-        this_split = qif_split_copy(split);
-        txn->default_split = this_split;
-
-        /* then adjust the 'far' txn */
-        split->amount = gnc_numeric_neg(split->amount);
-        split->value = split->amount;
-        txn->splits = g_list_prepend(txn->splits, split);
-    }
-
-    /* Set the default-split value from the default-split amount */
-    txn->default_split->value = txn->default_split->amount;
-}
-
-/* This is called when we're done processing an account.  We want
- * to merge the transactions in the "parse_state" into the Qif Context
- */
-static QifError
-qif_txn_end_acct(QifContext ctx)
-{
-    GList *node;
-    QifTxn txn;
-    gboolean txn_needs_acct;
-
-    g_return_val_if_fail(ctx, QIF_E_INTERNAL);
-
-    /* Return now if there is nothing to do. */
-    if (!ctx->parse_state) return QIF_E_OK;
-
-    /* Walk through the list of transactions.  First check if it
-     * needs a from-account; then add it to the context.
-     */
-
-    txn_needs_acct = (ctx->parse_flags & QIF_F_TXN_NEEDS_ACCT);
-
-    /* Invert the list so we're working in the right order */
-    ctx->parse_state = g_list_reverse(ctx->parse_state);
-
-    for (node = ctx->parse_state; node; node = node->next)
-    {
-        txn = node->data;
-
-        /* If we need a from account, then set it.. */
-        if (txn_needs_acct && ctx->opening_bal_acct && !txn->from_acct)
-            txn->from_acct = ctx->opening_bal_acct;
-
-        /* merge the txn into the context (prepends to the list) */
-        qif_object_list_insert(ctx, (QifObject)txn);
-    }
-
-    if (txn_needs_acct && ctx->opening_bal_acct)
-        qif_clear_flag(ctx->parse_flags, QIF_F_TXN_NEEDS_ACCT);
-
-    /* clean up our state */
-    g_list_free(ctx->parse_state);
-    ctx->parse_state = NULL;
-
-    return QIF_E_OK;
-}
-
-/* Extra info in an Investment Transaction */
-static QifInvstTxn
-qif_invst_txn_new(void)
-{
-    QifInvstTxn itxn = g_new0(struct _QifInvstTxn, 1);
-
-    itxn->amount = gnc_numeric_zero();
-    itxn->d_amount = gnc_numeric_zero();
-    itxn->price = gnc_numeric_zero();
-    itxn->shares = gnc_numeric_zero();
-    itxn->commission = gnc_numeric_zero();
-
-    return itxn;
-}
-
-static void
-qif_txn_invst_destroy(QifInvstTxn itxn)
-{
-    if (!itxn) return;
-
-    g_free(itxn->amountstr);
-    g_free(itxn->d_amountstr);
-    g_free(itxn->pricestr);
-    g_free(itxn->sharesstr);
-    g_free(itxn->commissionstr);
-    g_free(itxn->security);
-
-    g_free(itxn->catstr);
-
-    g_free(itxn);
-}
-
-static QifError
-qif_txn_invst_parse(QifContext ctx, GList *record)
-{
-    QifTxn txn;
-    QifInvstTxn itxn;
-    QifLine line;
-
-    g_return_val_if_fail(ctx, QIF_E_INTERNAL);
-    g_return_val_if_fail(record, QIF_E_BADSTATE);
-
-    txn = qif_txn_new();
-    txn->txn_type = ctx->parse_type;
-    itxn = qif_invst_txn_new();
-    txn->invst_info = itxn;
-
-    for (; record; record = record->next)
-    {
-        line = record->data;
-
-        switch (line->type)
-        {
-        case 'D':			/* D : transaction date */
-            qif_save_str(txn->datestr);
-            break;
-        case 'P':			/* P : txn payee */
-            qif_save_str(txn->payee);
-            break;
-        case 'N':			/* N : action */
-            itxn->action = qif_parse_action(line);
-            break;
-        case 'C':			/* C : cleared flag */
-            txn->cleared = qif_parse_cleared(line);
-            break;
-        case 'M':			/* M : memo */
-            if (!txn->current_split)
-                qif_save_str(txn->default_split->memo);
-            break;
-        case 'T':			/* T : total amount */
-            if (!qif_is_bad_numeric_string(line->line))
-                qif_save_str(itxn->amountstr);
-            break;
-        case '$':			/* $ : transfer amount */
-            if (!qif_is_bad_numeric_string(line->line))
-                qif_save_str(itxn->d_amountstr);
-            break;
-        case 'I':			/* I : share price */
-            qif_save_str(itxn->pricestr);
-            break;
-        case 'Q':			/* Q : number of shares */
-            qif_save_str(itxn->sharesstr);
-            break;
-        case 'Y':			/* Y : name of security */
-            qif_save_str(itxn->security);
-            break;
-        case 'O':			/* O : commission */
-            qif_save_str(itxn->commissionstr);
-            break;
-        case 'L':			/* L : category */
-            qif_save_str(itxn->catstr);
-            break;
-        default:
-            PERR("Unknown QIF Investment transaction data at line %d: %s",
-                 line->lineno, line->line);
-        }
-    }
-
-    /* If we have no date string then there is no reason to do anything else */
-    if (txn->datestr && itxn->action != QIF_A_NONE)
-    {
-
-        /* Make sure we've got a security name */
-        if (!itxn->security)
-            itxn->security = g_strdup("");	/* XXX */
-
-        /* if we don't have a from account, then mark the fact that
-         * we'll need one later.
-         */
-        if (ctx->current_acct)
-            txn->from_acct = ctx->current_acct;
-        else
-            qif_set_flag(ctx->parse_flags, QIF_F_ITXN_NEEDS_ACCT);
-
-        /* Add this transaction to the parse state for later processing */
-        ctx->parse_state = g_list_prepend(ctx->parse_state, txn);
-
-    }
-    else
-    {
-        /* no date?  Just destroy it */
-        qif_txn_destroy((QifObject)txn);
-    }
-
-    return QIF_E_OK;
-}
-
-
-void
-qif_invst_txn_setup_splits(QifContext ctx, QifTxn txn)
-{
-    QifInvstTxn itxn;
-    QifSplit near_split, far_split, comm_split;
-    QifAccount from_acct;
-
-    char *cat = NULL;
-    char *cat_class = NULL;
-    gboolean cat_is_acct = FALSE;
-    char *miscx = NULL;
-    char *miscx_class = NULL;
-    gboolean miscx_is_acct = FALSE;
-
-    /* Cached account-type lists */
-    static GList *bank_list = NULL;
-
-    gnc_numeric split_value;
-
-    g_return_if_fail(ctx);
-    g_return_if_fail(txn);
-    g_return_if_fail(txn->invst_info);
-
-    itxn = txn->invst_info;
-
-    /* Compute the share value, because we'll probably need it */
-    split_value = gnc_numeric_mul(itxn->shares, itxn->price, 0, GNC_HOW_DENOM_REDUCE);
-
-    /* Make sure that "amount" is a valid "transaction amount" */
-    if (!itxn->amountstr && itxn->d_amountstr)
-        itxn->amount = itxn->d_amount;
-
-    /* near and far splits..  for simplicity */
-    near_split = txn->default_split;
-    far_split = qif_split_new();
-    from_acct = txn->from_acct;
-
-    /* Parse the category string */
-    if (!qif_parse_split_category(itxn->catstr,
-                                  &cat, &cat_is_acct, &cat_class,
-                                  &miscx, &miscx_is_acct, &miscx_class))
-        PERR("Failure parsing category: %s", itxn->catstr);
-
-    /* Make sure we've got a cached list */
-    if (bank_list == NULL)
-        bank_list = qif_parse_acct_type("__any_bank__", -1);
-
-    /* find the NEAR account */
-
-    switch (itxn->action)
-    {
-    case QIF_A_BUY:
-    case QIF_A_BUYX:
-    case QIF_A_REINVDIV:
-    case QIF_A_REINVINT:
-    case QIF_A_REINVLG:
-    case QIF_A_REINVMD:
-    case QIF_A_REINVSG:
-    case QIF_A_REINVSH:
-    case QIF_A_SELL:
-    case QIF_A_SELLX:
-    case QIF_A_SHRSIN:
-    case QIF_A_SHRSOUT:
-    case QIF_A_STKSPLIT:
-        txn->from_acct = qif_default_stock_acct(ctx, itxn->security);
-        break;
-
-    case QIF_A_CGLONG:
-    case QIF_A_CGMID:
-    case QIF_A_CGSHORT:
-    case QIF_A_DIV:
-    case QIF_A_INTINC:
-    case QIF_A_MARGINT:
-    case QIF_A_MISCEXP:
-    case QIF_A_MISCINC:
-    case QIF_A_RTRNCAP:
-    case QIF_A_XIN:
-    case QIF_A_XOUT:
-        txn->from_acct = from_acct;
-        break;
-
-    case QIF_A_CGLONGX:
-    case QIF_A_CGMIDX:
-    case QIF_A_CGSHORTX:
-    case QIF_A_DIVX:
-    case QIF_A_INTINCX:
-    case QIF_A_MARGINTX:
-    case QIF_A_RTRNCAPX:
-        txn->from_acct = find_or_make_acct(ctx, cat, bank_list);
-        cat = NULL;
-        break;
-
-    case QIF_A_MISCEXPX:
-    case QIF_A_MISCINCX:
-        txn->from_acct = find_or_make_acct(ctx, miscx, bank_list);
-        miscx = NULL;
-        break;
-
-    default:
-        PERR("Unhandled Action: %d", itxn->action);
-        break;
-    }
-
-    /* find the FAR account */
-
-    itxn->far_cat_is_acct = TRUE;
-    switch (itxn->action)
-    {
-    case QIF_A_BUY:
-    case QIF_A_SELL:
-        itxn->far_cat.acct = from_acct;
-        break;
-
-    case QIF_A_BUYX:
-    case QIF_A_MISCEXP:
-    case QIF_A_MISCEXPX:
-    case QIF_A_MISCINC:
-    case QIF_A_MISCINCX:
-    case QIF_A_SELLX:
-    case QIF_A_XIN:
-    case QIF_A_XOUT:
-        itxn->far_cat.cat = find_or_make_cat(ctx, cat);
-        itxn->far_cat_is_acct = FALSE;
-        cat = NULL;
-        break;
-
-    case QIF_A_CGLONG:
-    case QIF_A_CGLONGX:
-    case QIF_A_REINVLG:
-        itxn->far_cat.acct = qif_default_cglong_acct(ctx, itxn->security);
-        break;
-
-    case QIF_A_CGMID:
-    case QIF_A_CGMIDX:
-    case QIF_A_REINVMD:
-        itxn->far_cat.acct = qif_default_cgmid_acct(ctx, itxn->security);
-        break;
-
-    case QIF_A_CGSHORT:
-    case QIF_A_CGSHORTX:
-    case QIF_A_REINVSG:
-    case QIF_A_REINVSH:
-        itxn->far_cat.acct = qif_default_cgshort_acct(ctx, itxn->security);
-        break;
-
-    case QIF_A_DIV:
-    case QIF_A_DIVX:
-    case QIF_A_REINVDIV:
-        itxn->far_cat.acct = qif_default_dividend_acct(ctx, itxn->security);
-        break;
-
-    case QIF_A_INTINC:
-    case QIF_A_INTINCX:
-    case QIF_A_REINVINT:
-        itxn->far_cat.acct = qif_default_interest_acct(ctx, itxn->security);
-        break;
-
-    case QIF_A_MARGINT:
-    case QIF_A_MARGINTX:
-        itxn->far_cat.acct = qif_default_margin_interest_acct(ctx);
-        break;
-
-    case QIF_A_RTRNCAP:
-    case QIF_A_RTRNCAPX:
-        itxn->far_cat.acct = qif_default_capital_return_acct(ctx, itxn->security);
-        break;
-
-    case QIF_A_SHRSIN:
-    case QIF_A_SHRSOUT:
-        itxn->far_cat.acct = qif_default_equity_holding(ctx, itxn->security);
-        break;
-
-    case QIF_A_STKSPLIT:
-        itxn->far_cat.acct = qif_default_stock_acct(ctx, itxn->security);
-        break;
-
-    default:
-        break;
-    }
-
-    /* If we don't have a far acct (or far category) then reset the flag */
-    if (!itxn->far_cat.obj)
-        itxn->far_cat_is_acct = FALSE;
-
-    /* And now fill in the "near" and "far" splits.  In particular we need
-     *
-     *	NEAR:	txn->from_acct, near_split->amount, value
-     *	FAR:	cat, far_split->amount, value
-     */
-    switch (itxn->action)
-    {
-    case QIF_A_BUY:
-    case QIF_A_BUYX:
-    case QIF_A_REINVDIV:
-    case QIF_A_REINVINT:
-    case QIF_A_REINVLG:
-    case QIF_A_REINVMD:
-    case QIF_A_REINVSG:
-    case QIF_A_REINVSH:
-    case QIF_A_SHRSIN:
-        near_split->amount = itxn->shares;
-        near_split->value = split_value;
-        far_split->amount = far_split->value = gnc_numeric_neg(itxn->amount);
-        break;
-
-    case QIF_A_SELL:
-    case QIF_A_SELLX:
-    case QIF_A_SHRSOUT:
-        near_split->amount = gnc_numeric_neg(itxn->shares);
-        near_split->value = gnc_numeric_neg(split_value);
-        far_split->amount = far_split->value = itxn->amount;
-        break;
-
-    case QIF_A_CGLONG:
-    case QIF_A_CGLONGX:
-    case QIF_A_CGMID:
-    case QIF_A_CGMIDX:
-    case QIF_A_CGSHORT:
-    case QIF_A_CGSHORTX:
-    case QIF_A_DIV:
-    case QIF_A_DIVX:
-    case QIF_A_INTINC:
-    case QIF_A_INTINCX:
-    case QIF_A_MISCINC:
-    case QIF_A_MISCINCX:
-    case QIF_A_RTRNCAP:
-    case QIF_A_RTRNCAPX:
-    case QIF_A_XIN:
-        near_split->amount = near_split->value = itxn->amount;
-        far_split->amount = far_split->value = gnc_numeric_neg(itxn->amount);
-        break;
-
-    case QIF_A_MARGINT:
-    case QIF_A_MARGINTX:
-    case QIF_A_MISCEXP:
-    case QIF_A_MISCEXPX:
-    case QIF_A_XOUT:
-        near_split->amount = near_split->value = gnc_numeric_neg(itxn->amount);
-        far_split->amount = far_split->value = itxn->amount;
-        break;
-
-    case QIF_A_STKSPLIT:
-        /* QIF just specifies the split ratio, not the number of shares
-         * in and out, so we have to fetch the number of shares from the
-         * security account..  FEH!
-         */
-
-        near_split->value = gnc_numeric_neg(split_value);
-        far_split->value = split_value;
-
-        /* XXX: FIXME: compute in-shares/out-shares based on ratio here:
-         *
-         * splitratio = num-shares / 10;
-         * in_shares = gnc_account_get_balance(near_acct);
-         * out_shares = in_shares * splitratio;
-         *
-         * near_split->amount = out_shares;
-         * far_split->amount = gnc_numeric_neg(in_shares);
-         *
-         * We know (later) that near_split == txn->default_split and
-         * far_split == txn->splits->data, so we'll just special-case this
-         * kind of txn when we convert to GNC later.
-         */
-
-        break;
-
-    default:
-        break;
-    }
-
-    /* Just make sure to set that it's an account, not a category */
-    far_split->cat.obj = itxn->far_cat.obj;
-    if (itxn->far_cat_is_acct)
-        far_split->cat_is_acct = TRUE;
-
-    /* make the commission split if we need it, then add it to the split-list  */
-    if (itxn->commissionstr)
-    {
-        comm_split = qif_split_new();
-        comm_split->cat.acct = qif_default_commission_acct(ctx);
-        comm_split->cat_is_acct = TRUE;
-        comm_split->amount = itxn->commission;
-        comm_split->value = itxn->commission;
-
-        txn->splits = g_list_prepend(txn->splits, comm_split);
-    }
-
-    /* Push the "far split" into the txn split-list */
-    txn->splits = g_list_prepend(txn->splits, far_split);
-
-    /* Free parsed strings.. */
-    g_free(cat);
-    g_free(cat_class);
-    g_free(miscx);
-    g_free(miscx_class);
-}
-
-
-/* Other handlers */
-static void
-qif_autoswitch_set(QifContext ctx)
-{
-    qif_set_flag(ctx->parse_flags, QIF_F_IGNORE_ACCOUNTS);
-}
-
-static void
-qif_autoswitch_clear(QifContext ctx)
-{
-    qif_clear_flag(ctx->parse_flags, QIF_F_IGNORE_ACCOUNTS);
-}
-
-/********************************************************************************
- * find or make ...
- */
-
-QifAccount
-find_or_make_acct(QifContext ctx, char *name, GList *types)
-{
-    QifAccount res;
-
-    res = (QifAccount)qif_object_map_lookup(ctx, QIF_O_ACCOUNT, name);
-    if (res)
-        g_free(name);
-    else
-    {
-        res = qif_account_new();
-        res->name = name;
-        res->type_list = types;
-
-        qif_object_map_insert(ctx, name, (QifObject)res);
-    }
-
-    return res;
-}
-
-QifCategory
-find_or_make_cat(QifContext ctx, char *name)
-{
-    QifCategory res;
-
-    res = (QifCategory)qif_object_map_lookup(ctx, QIF_O_CATEGORY, name);
-    if (res)
-        g_free(name);
-    else
-    {
-        res = qif_cat_new();
-
-        res->name = name;
-
-        qif_object_map_insert(ctx, name, (QifObject)res);
-    }
-
-    return res;
-}
-
-QifClass
-find_or_make_class(QifContext ctx, char *name)
-{
-    QifClass res;
-
-    res = (QifClass)qif_object_map_lookup(ctx, QIF_O_CLASS, name);
-    if (res)
-        g_free(name);
-    else
-    {
-        res = qif_class_new();
-        res->name = name;
-        qif_object_map_insert(ctx, name, (QifObject)res);
-    }
-    return res;
-}
-
-/*****************************************************************************/
-
-/*
- * initialize handlers
- */
-void
-qif_object_init(void)
-{
-    int i;
-    static struct
-    {
-        QifType		type;
-        struct _QifHandler	handler;
-    } handlers[] =
-    {
-        { QIF_TYPE_BANK, { qif_txn_init, qif_txn_parse, qif_txn_end_acct } },
-        { QIF_TYPE_CASH, { qif_txn_init, qif_txn_parse, qif_txn_end_acct } },
-        { QIF_TYPE_CCARD, { qif_txn_init, qif_txn_parse, qif_txn_end_acct } },
-        { QIF_TYPE_INVST, { qif_txn_init, qif_txn_invst_parse, qif_txn_end_acct } },
-        { QIF_TYPE_PORT, { qif_txn_init, qif_txn_invst_parse, qif_txn_end_acct } },
-        { QIF_TYPE_OTH_A, { qif_txn_init, qif_txn_parse, qif_txn_end_acct } },
-        { QIF_TYPE_OTH_L, { qif_txn_init, qif_txn_parse, qif_txn_end_acct } },
-        { QIF_TYPE_CLASS, { NULL, qif_class_parse, NULL } },
-        { QIF_TYPE_CAT, { NULL, qif_cat_parse, NULL } },
-        { QIF_TYPE_SECURITY, { NULL, qif_security_parse, NULL } },
-        { QIF_ACCOUNT, { NULL, qif_account_parse, NULL } },
-        { QIF_AUTOSWITCH, { qif_autoswitch_set, NULL, NULL } },
-        { QIF_CLEAR_AUTOSWITCH, { qif_autoswitch_clear, NULL, NULL } },
-        { 0, {NULL, NULL, NULL} }
-    };
-
-    for (i = 0; handlers[i].type > 0; i++)
-    {
-        if (handlers[i].type <= 0)
-        {
-            PERR("Invalid type?!?  (%d @ %d)", handlers[i].type, i);
-        }
-        else
-            qif_register_handler(handlers[i].type, &(handlers[i].handler));
-    }
-}
diff --git a/gnucash/import-export/qif/qif-objects.h b/gnucash/import-export/qif/qif-objects.h
deleted file mode 100644
index 2d28e59..0000000
--- a/gnucash/import-export/qif/qif-objects.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * qif-objects.h -- QIF objects for the QIF importer
- *
- * Written By:	Derek Atkins <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#ifndef QIF_OBJECTS_H
-#define QIF_OBJECTS_H
-
-typedef struct _QifObject *QifObject;
-typedef struct _QifData *QifData;
-
-struct _QifObject
-{
-    const char*	type;
-    void		(*destroy)(QifObject);
-
-    /* QIF Objects contain data beyond this point.. */
-};
-
-#define QIF_O_ACCOUNT	"qif-acct"
-typedef struct _QifAccount *QifAccount;
-
-#define QIF_O_CATEGORY	"qif-cat"
-typedef struct _QifCategory *QifCategory;
-
-#define QIF_O_CLASS	"qif-class"
-typedef struct _QifClass *QifClass;
-
-#define QIF_O_SECURITY	"qif-security"
-typedef struct _QifSecurity *QifSecurity;
-
-#define QIF_O_TXN	"qif-txn"
-typedef struct _QifTxn *QifTxn;
-typedef struct _QifSplit *QifSplit;
-typedef struct _QifInvstTxn *QifInvstTxn;
-
-void qif_object_init(void);
-
-QifAccount find_or_make_acct(QifContext ctx, char *name, GList *types);
-QifCategory find_or_make_cat(QifContext ctx, char *name);
-QifClass find_or_make_class(QifContext ctx, char *name);
-
-/* merge the object into the context.  Returns the object that's in
- * the context, which is either the supplied object or the
- * already-existing object.
- */
-QifAccount qif_account_merge(QifContext ctx, QifAccount acct);
-QifCategory qif_cat_merge(QifContext ctx, QifCategory cat);
-QifClass qif_class_merge(QifContext ctx, QifClass qclass);
-QifSecurity qif_security_merge(QifContext ctx, QifSecurity security);
-
-#endif /* QIF_OBJECTS_H */
diff --git a/gnucash/import-export/qif/qif-parse.c b/gnucash/import-export/qif/qif-parse.c
deleted file mode 100644
index f291c86..0000000
--- a/gnucash/import-export/qif/qif-parse.c
+++ /dev/null
@@ -1,935 +0,0 @@
-/*
- * qif-parse.c -- parse QIF
- *
- * Written by:        Derek Atkins <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <glib.h>
-#include <glib/gi18n.h>
-#include <string.h>
-
-/* For regex */
-#include <sys/types.h>
-#include <regex.h>
-
-#include <stdarg.h>
-
-#include "gnc-engine.h"
-#include "gnc-ui-util.h"
-
-#include "qif-import-p.h"
-#include "qif-objects-p.h"
-
-#include "import-parse.h"
-
-static QofLogModule log_module = GNC_MOD_IMPORT;
-
-/* An array of handlers for the various bang-types */
-static QifHandler qif_handlers[QIF_TYPE_MAX+1] = { NULL };
-
-/* Parser Regular Expressions */
-static gboolean qifp_regex_compiled = FALSE;
-static regex_t category_regex;
-
-/* A Hash Table of bang-types */
-static GHashTable *qif_bangtype_map = NULL;
-
-/* A Hash Table of action strings */
-static GHashTable *qif_action_map = NULL;
-
-/* A Hash Table of account types */
-static GHashTable *qif_atype_map = NULL;
-
-/************************************************************************/
-
-/* Register a handler */
-void
-qif_register_handler(QifType type, QifHandler handler)
-{
-    if (type <= 0 || type > QIF_TYPE_MAX)
-    {
-        PERR("Invalid type: %d", type);
-        return;
-    }
-    qif_handlers[type] = handler;
-}
-
-static void
-compile_regex()
-{
-    regcomp(&category_regex,
-            "^ *(\\[)?([^]/|]*)(]?)(/([^|]*))?(\\|(\\[)?([^]/]*)(]?)(/(.*))?)? *$",
-            REG_EXTENDED);
-
-    qifp_regex_compiled = TRUE;
-}
-
-#define QIF_ADD_TYPE(ts,t) \
-        g_hash_table_insert(qif_bangtype_map, ts, GINT_TO_POINTER(t)); \
-        g_hash_table_insert(qif_bangtype_map, _(ts), GINT_TO_POINTER(t));
-
-static void
-build_bangtype_map()
-{
-    g_return_if_fail(!qif_bangtype_map);
-
-    qif_bangtype_map = g_hash_table_new(g_str_hash, g_str_equal);
-    g_assert(qif_bangtype_map);
-
-    /* Translators FIXME: It is unclear whether these strings should
-       really be translated, and if yes, into which translation. */
-    QIF_ADD_TYPE(N_("type:bank"), QIF_TYPE_BANK);
-    QIF_ADD_TYPE(N_("type:cash"), QIF_TYPE_CASH);
-    QIF_ADD_TYPE(N_("type:ccard"), QIF_TYPE_CCARD);
-    QIF_ADD_TYPE(N_("type:invst"), QIF_TYPE_INVST);
-    QIF_ADD_TYPE(N_("type:port"), QIF_TYPE_PORT);
-    QIF_ADD_TYPE(N_("type:oth a"), QIF_TYPE_OTH_A);
-    QIF_ADD_TYPE(N_("type:oth l"), QIF_TYPE_OTH_L);
-    QIF_ADD_TYPE(N_("type:class"), QIF_TYPE_CLASS);
-    QIF_ADD_TYPE(N_("type:cat"), QIF_TYPE_CAT);
-    QIF_ADD_TYPE(N_("type:security"), QIF_TYPE_SECURITY);
-    QIF_ADD_TYPE(N_("account"), QIF_ACCOUNT);
-    QIF_ADD_TYPE(N_("option:autoswitch"), QIF_AUTOSWITCH);
-    QIF_ADD_TYPE(N_("clear:autoswitch"), QIF_CLEAR_AUTOSWITCH);
-}
-#undef QIF_ADD_TYPE
-
-#define QIF_ADD_ACT(ts,t) \
-        g_hash_table_insert(qif_action_map, ts, GINT_TO_POINTER(t));
-
-static void
-build_action_map()
-{
-    g_return_if_fail(!qif_action_map);
-
-    qif_action_map = g_hash_table_new(g_str_hash, g_str_equal);
-    g_assert(qif_action_map);
-
-    QIF_ADD_ACT("buy", QIF_A_BUY);
-    QIF_ADD_ACT("cvrshrt", QIF_A_BUY);
-    QIF_ADD_ACT("kauf", QIF_A_BUY);
-    QIF_ADD_ACT("buyx", QIF_A_BUYX);
-    QIF_ADD_ACT("cvrshrtx", QIF_A_BUYX);
-    QIF_ADD_ACT("kaufx", QIF_A_BUYX);
-    QIF_ADD_ACT("cglong", QIF_A_CGLONG);
-    QIF_ADD_ACT("kapgew", QIF_A_CGLONG); /* Kapitalgewinnsteuer */
-    QIF_ADD_ACT("cglongx", QIF_A_CGLONG);
-    QIF_ADD_ACT("kapgewx", QIF_A_CGLONG);
-    QIF_ADD_ACT("cgmid", QIF_A_CGMID);
-    QIF_ADD_ACT("cgmidx", QIF_A_CGMIDX);
-    QIF_ADD_ACT("cgshort", QIF_A_CGSHORT);
-    QIF_ADD_ACT("k.gewsp", QIF_A_CGSHORT);
-    QIF_ADD_ACT("cgshortx", QIF_A_CGSHORTX);
-    QIF_ADD_ACT("k.gewspx", QIF_A_CGSHORTX);
-    QIF_ADD_ACT("div", QIF_A_DIV); /* dividende */
-    QIF_ADD_ACT("divx", QIF_A_DIVX);
-    //QIF_ADD_ACT("exercise", QIF_A_EXERCISE);
-    //QIF_ADD_ACT("exercisex", QIF_A_EXERCISEX);
-    //QIF_ADD_ACT("expire", QIF_A_EXPIRE);
-    //QIF_ADD_ACT("grant", QIF_A_GRANT);
-    QIF_ADD_ACT("int", QIF_A_INTINC);
-    QIF_ADD_ACT("intinc", QIF_A_INTINC);
-    QIF_ADD_ACT("aktzu", QIF_A_INTINC); /* zinsen */
-    QIF_ADD_ACT("intx", QIF_A_INTINCX);
-    QIF_ADD_ACT("intincx", QIF_A_INTINCX);
-    QIF_ADD_ACT("margint", QIF_A_MARGINT);
-    QIF_ADD_ACT("margintx", QIF_A_MARGINTX);
-    QIF_ADD_ACT("miscexp", QIF_A_MISCEXP);
-    QIF_ADD_ACT("miscexpx", QIF_A_MISCEXPX);
-    QIF_ADD_ACT("miscinc", QIF_A_MISCINC);
-    QIF_ADD_ACT("cash", QIF_A_MISCINC);
-    QIF_ADD_ACT("miscincx", QIF_A_MISCINCX);
-    QIF_ADD_ACT("reinvdiv", QIF_A_REINVDIV);
-    QIF_ADD_ACT("reinvint", QIF_A_REINVINT);
-    QIF_ADD_ACT("reinvzin", QIF_A_REINVINT);
-    QIF_ADD_ACT("reinvlg", QIF_A_REINVLG);
-    QIF_ADD_ACT("reinvkur", QIF_A_REINVLG);
-    QIF_ADD_ACT("reinvmd", QIF_A_REINVMD);
-    QIF_ADD_ACT("reinvsg", QIF_A_REINVSG);
-    QIF_ADD_ACT("reinvksp", QIF_A_REINVSG);
-    QIF_ADD_ACT("reinvsh", QIF_A_REINVSH);
-    QIF_ADD_ACT("reminder", QIF_A_REMINDER);
-    QIF_ADD_ACT("erinnerg", QIF_A_REMINDER);
-    QIF_ADD_ACT("rtrncap", QIF_A_RTRNCAP);
-    QIF_ADD_ACT("rtrncapx", QIF_A_RTRNCAPX);
-    QIF_ADD_ACT("sell", QIF_A_SELL);
-    QIF_ADD_ACT("shtsell", QIF_A_SELL);
-    QIF_ADD_ACT("verkauf", QIF_A_SELL); /* verkaufen */
-    QIF_ADD_ACT("sellx", QIF_A_SELLX);
-    QIF_ADD_ACT("shtsellx", QIF_A_SELLX);
-    QIF_ADD_ACT("verkaufx", QIF_A_SELLX); /* verkaufen */
-    QIF_ADD_ACT("shrsin", QIF_A_SHRSIN);
-    QIF_ADD_ACT("aktzu", QIF_A_SHRSIN);
-    QIF_ADD_ACT("shrsout", QIF_A_SHRSOUT);
-    QIF_ADD_ACT("aktab", QIF_A_SHRSOUT);
-    QIF_ADD_ACT("stksplit", QIF_A_STKSPLIT);
-    QIF_ADD_ACT("aktsplit", QIF_A_STKSPLIT);
-    //QIF_ADD_ACT("vest", QIF_A_VEST);
-    QIF_ADD_ACT("xin", QIF_A_XIN);
-    QIF_ADD_ACT("contribx", QIF_A_XIN);
-    QIF_ADD_ACT("xout", QIF_A_XOUT);
-    QIF_ADD_ACT("withdrwx", QIF_A_XOUT);
-}
-#undef QIF_ADD_ACT
-
-static GList *
-make_list(int count, ...)
-{
-    GList *result = NULL;
-    GNCAccountType type;
-    va_list ap;
-
-    va_start (ap, count);
-    while (count--)
-    {
-        type = va_arg (ap, GNCAccountType);
-        result = g_list_prepend (result, GINT_TO_POINTER(type));
-    }
-    va_end (ap);
-
-
-    return g_list_reverse(result);
-}
-
-#define QIF_ADD_ATYPE(a,t) g_hash_table_insert(qif_atype_map, a, t);
-static void
-build_atype_map()
-{
-    g_return_if_fail(!qif_atype_map);
-
-    qif_atype_map = g_hash_table_new(g_str_hash, g_str_equal);
-    g_assert(qif_atype_map);
-
-    QIF_ADD_ATYPE("bank", make_list(1, ACCT_TYPE_BANK));
-    QIF_ADD_ATYPE("port", make_list(1, ACCT_TYPE_BANK));
-    QIF_ADD_ATYPE("cash", make_list(1, ACCT_TYPE_CASH));
-    QIF_ADD_ATYPE("ccard", make_list(1, ACCT_TYPE_CREDIT));
-    QIF_ADD_ATYPE("invst", make_list(3, ACCT_TYPE_BANK, ACCT_TYPE_STOCK,
-                                     ACCT_TYPE_MUTUAL));
-    QIF_ADD_ATYPE("oth a", make_list(3, ACCT_TYPE_ASSET, ACCT_TYPE_BANK,
-                                     ACCT_TYPE_CASH));
-    QIF_ADD_ATYPE("oth l", make_list(2, ACCT_TYPE_LIABILITY, ACCT_TYPE_CREDIT));
-    QIF_ADD_ATYPE("mutual", make_list(3, ACCT_TYPE_BANK, ACCT_TYPE_MUTUAL,
-                                      ACCT_TYPE_STOCK));
-
-    /* Internal types */
-    QIF_ADD_ATYPE("__any_bank__", make_list(5, ACCT_TYPE_BANK, ACCT_TYPE_CREDIT,
-                                            ACCT_TYPE_CASH, ACCT_TYPE_ASSET,
-                                            ACCT_TYPE_LIABILITY));
-    QIF_ADD_ATYPE("__all__", make_list(7, ACCT_TYPE_BANK, ACCT_TYPE_CREDIT,
-                                       ACCT_TYPE_CASH, ACCT_TYPE_ASSET,
-                                       ACCT_TYPE_LIABILITY, ACCT_TYPE_STOCK,
-                                       ACCT_TYPE_MUTUAL));
-    QIF_ADD_ATYPE("__stock__", make_list(2, ACCT_TYPE_STOCK, ACCT_TYPE_MUTUAL));
-    QIF_ADD_ATYPE("__income__", make_list(1, ACCT_TYPE_INCOME));
-    QIF_ADD_ATYPE("__expense__", make_list(1, ACCT_TYPE_EXPENSE));
-    QIF_ADD_ATYPE("__equity__", make_list(1, ACCT_TYPE_EQUITY));
-}
-#undef QIF_ADD_ATYPE
-
-/************************************************************************/
-
-/*
- * We've got a !Type line.  Parse the line into the appropriate
- * type and then initialize the handler.
- */
-void
-qif_parse_bangtype(QifContext ctx, const char *line)
-{
-    QifType type;
-    char *bangtype;
-    gpointer result;
-
-    g_return_if_fail(line && *line == '!');
-
-    if (!qif_bangtype_map)
-        build_bangtype_map();
-
-    /* Make a local copy so we can manipulate it.
-     * - strip off leading/trailing whitespace
-     * - make it all lower case
-     */
-    bangtype = g_utf8_strdown(line + 1, -1);
-    g_strstrip(bangtype);
-
-    /* In some cases we get "!Type Bank" -- change the space to a colon */
-    if (!strncmp(bangtype, "type ", 5))
-        bangtype[5] = ':';
-
-    /* Lookup the bangtype in the map and then destroy the local copy */
-    result = g_hash_table_lookup(qif_bangtype_map, bangtype);
-    g_free(bangtype);
-
-    if (!result)
-    {
-        PWARN("Unknown bang-type at line %d: %s.  Ignored", ctx->lineno, line);
-        return;
-    }
-    type = GPOINTER_TO_INT(result);
-
-    /* Set the current context parse type and handler */
-    ctx->parse_type = type;
-    ctx->handler = qif_handlers[type];
-
-    /* now initialize this new parse type (if there's an init function) */
-    if (ctx->handler && ctx->handler->init)
-        ctx->handler->init(ctx);
-}
-
-/* returns TRUE if successful, FALSE if there is a problem */
-gboolean
-qif_parse_split_category(const char* str,
-                         char** cat, gboolean *cat_is_acct, char** cat_class,
-                         char** miscx_cat, gboolean *miscx_cat_is_acct,
-                         char **miscx_class)
-{
-    /* This is a pretty f**ked up string.  Basically it looks like:
-     *  ([)cat-or-acct(])(/(class))(|([)cat-of-acct(])(/ext))
-     *
-     * where data in parens is "optional" (depending on the context).
-     *
-     * examples from reality:
-     *
-     * category
-     * category:subcategory
-     * category/class
-     * category:subcat/class
-     * [account]
-     * [account]/class
-     *
-     * cat/cat-class|miscx-cat/miscx-class
-     */
-
-    regmatch_t pmatch[12];
-
-    g_return_val_if_fail(cat && cat_is_acct && cat_class &&
-                         miscx_cat && miscx_cat_is_acct && miscx_class, FALSE);
-
-
-    if (!qifp_regex_compiled)
-        compile_regex();
-
-    if (regexec(&category_regex, str, 12, pmatch, 0) != 0)
-    {
-        PERR("category match failed");
-        return FALSE;
-    }
-
-    /*
-     * what the substrings mean:
-     * 1 the opening [ for a transfer
-     * 2 the category
-     * 3 the closing ]
-     * 4 the class /
-     * 5 the class
-     * 6 the miscx expression (whole thing)
-     * 7 the opening [
-     * 8 the miscx category
-     * 9 the closing ]
-     * 10 the class /
-     * 11 the class
-     */
-
-    if (pmatch[2].rm_so == -1)
-    {
-        PERR("no category match found!");
-        return FALSE;
-    }
-
-    /* catgory name */
-    *cat = g_strndup(str + pmatch[2].rm_so, pmatch[2].rm_eo - pmatch[2].rm_so);
-    /* category is account? */
-    *cat_is_acct = (pmatch[1].rm_so != -1 && pmatch[3].rm_so != -1);
-    /* category class */
-    *cat_class = (pmatch[4].rm_so != -1 ?
-                  g_strndup(str + pmatch[5].rm_so, pmatch[5].rm_eo - pmatch[5].rm_so) :
-                  NULL);
-
-    /* miscx category name */
-    *miscx_cat = (pmatch[6].rm_so != -1 ?
-                  g_strndup(str + pmatch[8].rm_so, pmatch[8].rm_eo - pmatch[8].rm_so) :
-                  NULL);
-    /* miscx cat is acct */
-    *miscx_cat_is_acct  = (pmatch[7].rm_so != -1 && pmatch[9].rm_so != -1);
-    /* miscx class */
-    *miscx_class = (pmatch[10].rm_so != -1 ?
-                    g_strndup(str + pmatch[11].rm_so,
-                              pmatch[11].rm_eo - pmatch[11].rm_so) : NULL);
-
-    return TRUE;
-}
-
-/*
- * qif_parse_cleared -- parse the 'C'leared field of a QIF Transaction.
- * returns the QIF reconciled flag.
- *
- * * means cleared, x or X means reconciled, and ! or ? mean some
- * budget related stuff I don't understand.
- */
-QifRecnFlag
-qif_parse_cleared(QifLine line)
-{
-    g_return_val_if_fail(line, QIF_R_NO);
-    g_return_val_if_fail(line->line, QIF_R_NO);
-
-    switch (*line->line)
-    {
-    case '*':
-        return QIF_R_CLEARED;
-    case 'x':
-    case 'X':
-        return QIF_R_RECONCILED;
-    case '?':
-    case '!':
-        return QIF_R_BUDGETED;
-    default:
-        PERR("Unknown QIF Cleared flag at line %d: %s", line->lineno, line->line);
-        return QIF_R_NO;
-    }
-}
-
-QifAction qif_parse_action(QifLine line)
-{
-    QifAction qaction;
-    gpointer result;
-    char *action;
-
-    g_return_val_if_fail(line, QIF_A_NONE);
-    g_return_val_if_fail(line->line, QIF_A_NONE);
-
-    if (!qif_action_map)
-        build_action_map();
-
-    /* Duplicate the action and force it to lower case and strip any spaces */
-    action = g_utf8_strdown(line->line, -1);
-    g_strstrip(action);
-
-    result = g_hash_table_lookup(qif_action_map, action);
-    g_free(action);
-
-    if (!result)
-    {
-        /* XXX: pop up a dialog? */
-        PWARN("Unknown Action at line %d: %s.  Some transactions may be discarded",
-              line->lineno, line->line);
-        return QIF_A_NONE;
-    }
-
-    qaction = GPOINTER_TO_INT(result);
-    return qaction;
-}
-
-GList * qif_parse_acct_type(const char *str, gint lineno)
-{
-    GList *result;
-    char *type;
-
-    if (!qif_atype_map)
-        build_atype_map();
-
-    /* Duplicate the type and force it to lower case and strip any spaces */
-    type = g_utf8_strdown(str, -1);
-    g_strstrip(type);
-
-    result = g_hash_table_lookup(qif_atype_map, type);
-    g_free(type);
-
-    if (!result)
-    {
-        PWARN("Unknown account type at line %d: %s. ", lineno, str);
-        result = g_hash_table_lookup(qif_atype_map, "bank");
-        g_return_val_if_fail(result, NULL);
-    }
-
-    return result;
-}
-
-GList * qif_parse_acct_type_guess(QifType type)
-{
-    const char *atype = NULL;
-
-    switch (type)
-    {
-    case QIF_TYPE_BANK:
-        atype = "bank";
-        break;
-    case QIF_TYPE_CASH:
-        atype = "cash";
-        break;
-    case QIF_TYPE_CCARD:
-        atype = "ccard";
-        break;
-    case QIF_TYPE_INVST:
-        atype = "invst";
-        break;
-    case QIF_TYPE_PORT:
-        atype = "port";
-        break;
-    case QIF_TYPE_OTH_A:
-        atype = "oth a";
-        break;
-    case QIF_TYPE_OTH_L:
-        atype = "oth l";
-        break;
-    default:
-        return NULL;
-    }
-
-    return qif_parse_acct_type(atype, -1);
-}
-
-/***********************************************************************
- * Parsing numbers and dates...
- */
-
-typedef struct _parse_helper
-{
-    QifContext                ctx;
-
-    GncImportFormat        budget;
-    GncImportFormat        limit;
-    GncImportFormat        amount;
-    GncImportFormat        d_amount;
-    GncImportFormat        price;
-    GncImportFormat        shares;
-    GncImportFormat        commission;
-    GncImportFormat        date;
-} *parse_helper_t;
-
-#define QIF_PARSE_CHECK_NUMBER(str,help) { \
-        if (str) (help) = gnc_import_test_numeric((str), (help)); \
-}
-#define QIF_PARSE_PARSE_NUMBER(str,fmt,val) { \
-        if (str) gnc_import_parse_numeric((str), (fmt), (val)); \
-}
-
-static void
-qif_parse_check_account(gpointer key, gpointer val, gpointer data)
-{
-    parse_helper_t helper = data;
-    QifAccount acct = val;
-
-    QIF_PARSE_CHECK_NUMBER(acct->limitstr, helper->limit);
-    QIF_PARSE_CHECK_NUMBER(acct->budgetstr, helper->budget);
-}
-
-static void
-qif_parse_parse_account(gpointer key, gpointer val, gpointer data)
-{
-    parse_helper_t helper = data;
-    QifAccount acct = val;
-
-    QIF_PARSE_PARSE_NUMBER(acct->limitstr, helper->limit, &acct->limit);
-    QIF_PARSE_PARSE_NUMBER(acct->budgetstr, helper->budget, &acct->budget);
-}
-
-static void
-qif_parse_check_category(gpointer key, gpointer val, gpointer data)
-{
-    parse_helper_t helper = data;
-    QifCategory cat = val;
-
-    QIF_PARSE_CHECK_NUMBER(cat->budgetstr, helper->budget);
-}
-
-static void
-qif_parse_parse_category(gpointer key, gpointer val, gpointer data)
-{
-    parse_helper_t helper = data;
-    QifCategory cat = val;
-
-    QIF_PARSE_PARSE_NUMBER(cat->budgetstr, helper->budget, &cat->budget);
-}
-
-static void
-qif_parse_check_txn(gpointer val, gpointer data)
-{
-    parse_helper_t helper = data;
-    QifTxn txn = val;
-    QifSplit split;
-    QifInvstTxn itxn;
-    GList *node;
-
-    /* Check the date */
-    helper->date = gnc_import_test_date(txn->datestr, helper->date);
-
-    /* If this is an investment transaction, then all the info is in
-     * the invst_info.  Otherwise it's all in the splits.
-     */
-    itxn = txn->invst_info;
-    if (itxn)
-    {
-        QIF_PARSE_CHECK_NUMBER(itxn->amountstr, helper->amount);
-        QIF_PARSE_CHECK_NUMBER(itxn->d_amountstr, helper->d_amount);
-        QIF_PARSE_CHECK_NUMBER(itxn->pricestr, helper->price);
-        QIF_PARSE_CHECK_NUMBER(itxn->sharesstr, helper->shares);
-        QIF_PARSE_CHECK_NUMBER(itxn->commissionstr, helper->commission);
-
-    }
-    else
-    {
-        split = txn->default_split;
-        node = txn->splits;
-        do
-        {
-            QIF_PARSE_CHECK_NUMBER(split->amountstr, helper->amount);
-
-            if (node)
-            {
-                split = node->data;
-                node = node->next;
-            }
-            else
-                split = NULL;
-        }
-        while (split);
-    }
-}
-
-static void
-qif_parse_parse_txn(gpointer val, gpointer data)
-{
-    parse_helper_t helper = data;
-    QifTxn txn = val;
-    QifSplit split;
-    QifInvstTxn itxn;
-    GList *node;
-
-    /* Parse the date */
-    gnc_import_parse_date(txn->datestr, helper->date, &txn->date);
-
-    /* If this is an investment transaction, then all the info is in
-     * the invst_info.  Otherwise it's all in the splits.
-     */
-    itxn = txn->invst_info;
-    if (itxn)
-    {
-        QIF_PARSE_PARSE_NUMBER(itxn->amountstr, helper->amount, &itxn->amount);
-        QIF_PARSE_PARSE_NUMBER(itxn->d_amountstr, helper->d_amount, &itxn->d_amount);
-        QIF_PARSE_PARSE_NUMBER(itxn->pricestr, helper->price, &itxn->price);
-        QIF_PARSE_PARSE_NUMBER(itxn->sharesstr, helper->shares, &itxn->shares);
-        QIF_PARSE_PARSE_NUMBER(itxn->commissionstr, helper->commission,
-                               &itxn->commission);
-
-        qif_invst_txn_setup_splits(helper->ctx, txn);
-
-    }
-    else
-    {
-        split = txn->default_split;
-        node = txn->splits;
-        do
-        {
-            QIF_PARSE_PARSE_NUMBER(split->amountstr, helper->amount, &split->amount);
-
-            if (node)
-            {
-                split = node->data;
-                node = node->next;
-            }
-            else
-                split = NULL;
-        }
-        while (split);
-
-        qif_txn_setup_splits(txn);
-    }
-}
-
-void
-qif_parse_all(QifContext ctx, gpointer arg)
-{
-    struct _parse_helper helper;
-
-    helper.ctx = ctx;
-
-    /* PARSE ACCOUNTS */
-
-    /* First, figure out the formats */
-    helper.limit = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
-    helper.budget = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
-    qif_object_map_foreach(ctx, QIF_O_ACCOUNT, qif_parse_check_account, &helper);
-
-    /* Make sure it's not ambiguous */
-    if (helper.limit & (helper.limit - 1)) helper.limit = GNCIF_NUM_PERIOD;
-    if (helper.budget & (helper.budget - 1)) helper.budget = GNCIF_NUM_PERIOD;
-
-    /* Now convert the numbers */
-    qif_object_map_foreach(ctx, QIF_O_ACCOUNT, qif_parse_parse_account, &helper);
-
-    /* PARSE CATEGORIES */
-
-    helper.budget = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
-    qif_object_map_foreach(ctx, QIF_O_CATEGORY, qif_parse_check_category, &helper);
-
-    /* make sure it's not ambiguous */
-    if (helper.budget & (helper.budget - 1)) helper.budget = GNCIF_NUM_PERIOD;
-
-    /* Now convert the numbers */
-    qif_object_map_foreach(ctx, QIF_O_CATEGORY, qif_parse_parse_category, &helper);
-
-    /* PARSE TRANSACTIONS */
-    helper.amount = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
-    helper.d_amount = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
-    helper.price = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
-    helper.shares = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
-    helper.commission = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
-    helper.date = GNCIF_DATE_MDY | GNCIF_DATE_DMY | GNCIF_DATE_YMD | GNCIF_DATE_YDM;
-
-    qif_object_list_foreach(ctx, QIF_O_TXN, qif_parse_check_txn, &helper);
-
-    /* check/fix ambiguities */
-    if (helper.amount & (helper.amount - 1)) helper.amount = GNCIF_NUM_PERIOD;
-    if (helper.d_amount & (helper.d_amount - 1)) helper.d_amount = GNCIF_NUM_PERIOD;
-    if (helper.price & (helper.price - 1)) helper.price = GNCIF_NUM_PERIOD;
-    if (helper.shares & (helper.shares - 1)) helper.shares = GNCIF_NUM_PERIOD;
-    if (helper.commission & (helper.commission - 1))
-        helper.commission = GNCIF_NUM_PERIOD;
-
-    if (helper.date & (helper.date - 1))
-    {
-        helper.date = gnc_import_choose_fmt(_("The Date format is ambiguous.  "
-                                              "Please choose the correct format."),
-                                            helper.date, arg);
-    }
-
-    /* now parse it.. */
-    qif_object_list_foreach(ctx, QIF_O_TXN, qif_parse_parse_txn, &helper);
-}
-
-typedef struct
-{
-    QifContext        ctx;
-    GList *        list;
-    const char*        type;
-} qif_merge_t;
-
-static void
-qif_merge_accts(gpointer key, gpointer value, gpointer data)
-{
-    qif_merge_t *merge = data;
-    QifAccount acct = value;
-
-    /* Merge into the context.  Remember items moved into the parent */
-    if (qif_account_merge(merge->ctx, acct) == acct)
-        merge->list = g_list_prepend(merge->list, acct->name);
-}
-
-static void
-qif_merge_cats(gpointer key, gpointer value, gpointer data)
-{
-    qif_merge_t *merge = data;
-    QifCategory cat = value;
-
-    /* Merge into the context.  Remember items moved into the parent */
-    if (qif_cat_merge(merge->ctx, cat) == cat)
-        merge->list = g_list_prepend(merge->list, cat->name);
-}
-
-static void
-qif_merge_classes(gpointer key, gpointer value, gpointer data)
-{
-    qif_merge_t *merge = data;
-    QifClass qclass = value;
-
-    /* Merge into the context.  Remember items moved into the parent */
-    if (qif_class_merge(merge->ctx, qclass) == qclass)
-        merge->list = g_list_prepend(merge->list, qclass->name);
-}
-
-static void
-qif_merge_secs(gpointer key, gpointer value, gpointer data)
-{
-    qif_merge_t *merge = data;
-    QifSecurity sec = value;
-
-    /* Merge into the context.  Remember items moved into the parent */
-    if (qif_security_merge(merge->ctx, sec) == sec)
-        merge->list = g_list_prepend(merge->list, sec->name);
-}
-
-static void
-qif_merge_del(gpointer obj, gpointer data)
-{
-    qif_merge_t *merge = data;
-    const char *name = obj;
-
-    qif_object_map_remove(merge->ctx, merge->type, name);
-}
-
-static void
-qif_massage_split(QifSplit split, QifContext ctx)
-{
-    const char *type = QIF_O_CATEGORY;
-    char *name;
-
-    if (split->cat.obj)
-    {
-        if (split->cat_is_acct)
-        {
-            type = QIF_O_ACCOUNT;
-            name = split->cat.acct->name;
-        }
-        else
-            name = split->cat.cat->name;
-
-        split->cat.obj = qif_object_map_lookup(ctx, type, name);
-    }
-
-    if (split->cat_class)
-    {
-        split->cat_class = (QifClass) qif_object_map_lookup(ctx, QIF_O_CLASS,
-                           split->cat_class->name);
-    }
-}
-
-static void
-qif_massage_itxn(QifInvstTxn itxn, QifContext ctx)
-{
-    const char *type = QIF_O_CATEGORY;
-    char *name;
-
-    if (itxn->far_cat.obj)
-    {
-        if (itxn->far_cat_is_acct)
-        {
-            type = QIF_O_ACCOUNT;
-            name = itxn->far_cat.acct->name;
-        }
-        else
-            name = itxn->far_cat.cat->name;
-
-        itxn->far_cat.obj = qif_object_map_lookup(ctx, type, name);
-    }
-}
-
-static void
-qif_massage_txn(gpointer obj, gpointer data)
-{
-    QifTxn txn = obj;
-    QifContext ctx = data;
-    QifSplit split;
-    GList *node;
-
-    if (txn->from_acct)
-        txn->from_acct = (QifAccount) qif_object_map_lookup(ctx, QIF_O_ACCOUNT,
-                         txn->from_acct->name);
-
-    if (txn->invst_info)
-        qif_massage_itxn(txn->invst_info, ctx);
-
-    if (txn->default_split)
-        qif_massage_split(txn->default_split, ctx);
-
-    for (node = txn->splits; node; node = node->next)
-    {
-        split = node->data;
-        qif_massage_split(split, ctx);
-    }
-}
-
-void
-qif_parse_merge_files(QifContext ctx)
-{
-    GList *node;
-    GList *accts = NULL;
-    GList *cats = NULL;
-    GList *classes = NULL;
-    GList *securities = NULL;
-    QifContext fctx;
-
-    qif_merge_t merge;
-
-    g_return_if_fail(ctx);
-
-    /* Make sure each of the "file" contexts have been parsed.
-     * note that we don't care about OUR context -- we can run this
-     * process multiple times safely.
-     */
-    for (node = ctx->files; node; node = node->next)
-    {
-        fctx = node->data;
-        g_return_if_fail(fctx->parsed);
-    }
-
-
-    /* Iterate over each file.  Merge the Accounts, Categories, Classes,
-     * Securities, and Transactions into the top-level context.  Be sure
-     * to re-point all Transaction/Split category/class/account pointers
-     * to the new top-level item.  Then be sure to remove the
-     * "duplicated" items so we don't double-free (as we don't refcount,
-     * either).
-     */
-    for (node = ctx->files; node; node = node->next)
-    {
-        fctx = node->data;
-
-        /* Merge accts, categories, classes, and securities */
-
-        merge.ctx = ctx;
-        merge.list = NULL;
-        qif_object_map_foreach(fctx, QIF_O_ACCOUNT, qif_merge_accts, &merge);
-        accts = merge.list;
-
-        merge.list = NULL;
-        qif_object_map_foreach(fctx, QIF_O_CATEGORY, qif_merge_cats, &merge);
-        cats = merge.list;
-
-        merge.list = NULL;
-        qif_object_map_foreach(fctx, QIF_O_CLASS, qif_merge_classes, &merge);
-        classes = merge.list;
-
-        merge.list = NULL;
-        qif_object_map_foreach(fctx, QIF_O_SECURITY, qif_merge_secs, &merge);
-        securities = merge.list;
-
-
-        /* repoint the transactions to the merged context data */
-        qif_object_list_foreach(fctx, QIF_O_TXN, qif_massage_txn, ctx);
-
-
-        /* then remove from the file context objects referenced in the top context */
-        merge.ctx = fctx;
-        merge.type = QIF_O_ACCOUNT;
-        g_list_foreach(accts, qif_merge_del, &merge);
-        g_list_free(accts);
-
-        merge.type = QIF_O_CATEGORY;
-        g_list_foreach(cats, qif_merge_del, &merge);
-        g_list_free(cats);
-
-        merge.type = QIF_O_CLASS;
-        g_list_foreach(classes, qif_merge_del, &merge);
-        g_list_free(classes);
-
-        merge.type = QIF_O_SECURITY;
-        g_list_foreach(securities, qif_merge_del, &merge);
-        g_list_free(securities);
-
-    }
-
-    /* We've been parsed */
-    ctx->parsed = TRUE;
-}
diff --git a/gnucash/import-export/qif/qif-parse.h b/gnucash/import-export/qif/qif-parse.h
deleted file mode 100644
index 11d439c..0000000
--- a/gnucash/import-export/qif/qif-parse.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * qif-parse.h -- routines for parsing pieces of a QIF file
- *
- * Written By:	Derek Atkins  <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#ifndef QIF_PARSE_H
-#define QIF_PARSE_H
-
-#include "qif-import.h"
-
-void qif_register_handler(QifType type, QifHandler handler);
-void qif_parse_bangtype(QifContext ctx, const char *line);
-
-gboolean
-qif_parse_split_category(const char* str,
-                         char** cat, gboolean *cat_is_acct, char** cat_class,
-                         char** miscx_cat, gboolean *miscx_cat_is_acct,
-                         char **miscx_class);
-
-gboolean qif_parse_numeric(QifLine line, gnc_numeric *num);
-QifRecnFlag qif_parse_cleared(QifLine line);
-QifAction qif_parse_action(QifLine line);
-
-/* The caller should never destroy this list */
-GList * qif_parse_acct_type(const char *str, gint lineno);
-GList * qif_parse_acct_type_guess(QifType type);
-
-/* Parse all objects */
-void qif_parse_all(QifContext ctx, gpointer ui_args);
-
-#endif /* QIF_PARSE_H */
diff --git a/gnucash/import-export/qif/test/CMakeLists.txt b/gnucash/import-export/qif/test/CMakeLists.txt
deleted file mode 100644
index d874ca3..0000000
--- a/gnucash/import-export/qif/test/CMakeLists.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-
-set(QIF_TEST_INCLUDE_DIRS
-  ${CMAKE_BINARY_DIR}/common
-  ${CMAKE_SOURCE_DIR}/gnucash/import-export/qif
-  ${CMAKE_SOURCE_DIR}/libgnucash/engine
-  ${CMAKE_SOURCE_DIR}/common/test-core
-  ${GLIB2_INCLUDE_DIRS}
-)
-set(QIF_TEST_LIBS gncmod-qif test-core)
-
-if (FALSE)
-  # Tests for this directory are not run.
-  gnc_add_test(test-link-qif test-link.c QIF_TEST_INCLUDE_DIRS QIF_TEST_LIBS)
-  gnc_add_test(test-qif test-qif.c QIF_TEST_INCLUDE_DIRS QIF_TEST_LIBS
-    GNC_TEST_FILES=${CMAKE_CURRENT_SOURCE_DIR}/test-files)
-endif()
-
-set_dist_list(test_qif_DIST CMakeLists.txt test-link.c test-qif.c test-files/test-1-bank-txn.qif)
diff --git a/gnucash/import-export/qif/test/test-files/test-1-bank-txn.qif b/gnucash/import-export/qif/test/test-files/test-1-bank-txn.qif
deleted file mode 100644
index 59592d0..0000000
--- a/gnucash/import-export/qif/test/test-files/test-1-bank-txn.qif
+++ /dev/null
@@ -1,6 +0,0 @@
-!Type:Bank
-D2003/01/27
-T123.45
-PTest Payee
-LTest Category
-^
diff --git a/gnucash/import-export/qif/test/test-link.c b/gnucash/import-export/qif/test/test-link.c
deleted file mode 100644
index fd55d42..0000000
--- a/gnucash/import-export/qif/test/test-link.c
+++ /dev/null
@@ -1,28 +0,0 @@
-/********************************************************************\
- * This program is free software; you can redistribute it and/or    *
- * modify it under the terms of the GNU General Public License as   *
- * published by the Free Software Foundation; either version 2 of   *
- * the License, or (at your option) any later version.              *
- *                                                                  *
- * This program is distributed in the hope that it will be useful,  *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
- * GNU General Public License for more details.                     *
- *                                                                  *
- * You should have received a copy of the GNU General Public License*
- * along with this program; if not, contact:                        *
- *                                                                  *
- * Free Software Foundation           Voice:  +1-617-542-5942       *
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
- * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
- *                                                                  *
-\********************************************************************/
-
-#include "qif-import.h"
-
-int
-main(int argc, char *argv[])
-{
-    qif_context_new();
-    return 0;
-}
diff --git a/gnucash/import-export/qif/test/test-qif.c b/gnucash/import-export/qif/test/test-qif.c
deleted file mode 100644
index c331bef..0000000
--- a/gnucash/import-export/qif/test/test-qif.c
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * test-qif.c -- Test the QIF Import routines.
- *
- * Created by:	Derek Atkins <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#include <glib.h>
-#include <libguile.h>
-
-#include "gnc-module.h"
-#include "qif-import.h"
-#include "qif-import-p.h"	/* Let's test some internal stuff, too */
-
-#include "test-stuff.h"
-
-/* XXX */
-extern void qif_object_init(void);
-
-static QifContext
-test_qif_load_file(QifContext ctx, const char *filename,
-                   gint txn_count, gint acct_count, gboolean needs_acct)
-{
-    QifContext file;
-
-    printf("qif loading \"%s\"...\n", filename);
-    file = qif_file_new(ctx, filename);
-    do_test(file != NULL, "failed to read file");
-    if (!file) return NULL;
-
-    do_test(qif_object_list_count(file, QIF_O_TXN) == txn_count,
-            "Transaction count didn't match");
-    do_test(qif_object_map_count(file, QIF_O_ACCOUNT) == acct_count,
-            "Account count didn't match");
-    do_test(qif_file_needs_account(file) == needs_acct,
-            "Needs account flad didn't match");
-
-    return file;
-}
-
-static void
-test_qif(void)
-{
-    QifContext ctx, file;
-    char *filename;
-    const char *location = g_getenv("GNC_TEST_FILES");
-    int i;
-
-    ctx = qif_context_new();
-    do_test(ctx != NULL, "failed to create the qif context");
-    if (!ctx) return;
-
-    if (!location)
-        location = "test-files";
-
-    for (i = 0; i < 1; i++)
-    {
-        filename = g_strdup_printf("%s/%s", location, "test-1-bank-txn.qif");
-        file = test_qif_load_file(ctx, filename, 1, 0, TRUE);
-        g_free(filename);
-        if (!file) continue;
-
-        if (qif_file_needs_account(file))
-            qif_file_set_default_account(file, "test-1-bank-txn");
-
-        do_test(qif_file_needs_account(file) == FALSE,
-                "'Needs account' flag not cleared properly");
-
-        do_test(qif_file_parse(file, NULL) == QIF_E_OK,
-                "file failed to parse.");
-    }
-
-    qif_context_destroy(ctx);
-
-    success("QIF test successful");
-}
-
-static void
-main_helper(void *closure, int argc, char **argv)
-{
-    qif_object_init();		/* XXX:FIXME */
-    test_qif();
-    print_test_results();
-    exit(get_rv());
-}
-
-int
-main(int argc, char **argv)
-{
-    scm_boot_guile(argc, argv, main_helper, NULL);
-    return 0;
-}
-
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 426b3f2..ede339f 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -347,9 +347,7 @@ gnucash/import-export/ofx/gnc-ofx-import.c
 gnucash/import-export/ofx/gnc-ofx-kvp.c
 gnucash/import-export/ofx/gnc-plugin-ofx.c
 gnucash/import-export/ofx/gschemas/org.gnucash.dialogs.import.ofx.gschema.xml.in
-gnucash/import-export/qif/qif-context.c
-gnucash/import-export/qif/qif-file.c
-gnucash/import-export/qif/qif-objects.c
+gnucash/import-export/qif-imp/.#assistant-qif-import.c
 gnucash/import-export/qif-imp/assistant-qif-import.c
 gnucash/import-export/qif-imp/dialog-account-picker.c
 gnucash/import-export/qif-imp/gncmod-qif-import.c
@@ -486,8 +484,7 @@ gnucash/report/standard-reports/general-journal.scm
 gnucash/report/standard-reports/general-ledger.scm
 gnucash/report/standard-reports/income-gst-statement.scm
 gnucash/report/standard-reports/income-statement.scm
-gnucash/report/standard-reports/net-barchart.scm
-gnucash/report/standard-reports/net-linechart.scm
+gnucash/report/standard-reports/net-charts.scm
 gnucash/report/standard-reports/portfolio.scm
 gnucash/report/standard-reports/price-scatter.scm
 gnucash/report/standard-reports/register.scm
@@ -575,7 +572,6 @@ libgnucash/backend/sql/gnc-sql-result.cpp
 libgnucash/backend/sql/gnc-tax-table-sql.cpp
 libgnucash/backend/sql/gnc-transaction-sql.cpp
 libgnucash/backend/sql/gnc-vendor-sql.cpp
-libgnucash/backend/xml/.#gnc-invoice-xml-v2.cpp
 libgnucash/backend/xml/gnc-account-xml-v2.cpp
 libgnucash/backend/xml/gnc-address-xml-v2.cpp
 libgnucash/backend/xml/gnc-backend-xml.cpp

commit 9f1bfddc27965f50295f507b062d63b8612d1c9b
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Jun 15 11:30:09 2018 -0700

    Revert "Remove abandoned C-language QIF implementation."
    
    Because it included some extraneous changes.
    This reverts commit edd439a05e1468b1f55d0df7c05441b67aac8787.

diff --git a/gnucash/import-export/CMakeLists.txt b/gnucash/import-export/CMakeLists.txt
index cca8359..3b52ed8 100644
--- a/gnucash/import-export/CMakeLists.txt
+++ b/gnucash/import-export/CMakeLists.txt
@@ -10,6 +10,7 @@ add_subdirectory(csv-imp)
 add_subdirectory(customer-import)
 add_subdirectory(log-replay)
 add_subdirectory(ofx)
+add_subdirectory(qif)
 add_subdirectory(qif-imp)
 
 
diff --git a/gnucash/import-export/qif-imp/assistant-qif-import.c b/gnucash/import-export/qif-imp/assistant-qif-import.c
index e424182..21f3e74 100644
--- a/gnucash/import-export/qif-imp/assistant-qif-import.c
+++ b/gnucash/import-export/qif-imp/assistant-qif-import.c
@@ -59,7 +59,6 @@
 #include "gnc-prefs.h"
 #include "gnc-ui.h"
 #include "guile-mappings.h"
-#include <gfec.h>
 
 #include "swig-runtime.h"
 
@@ -1068,12 +1067,6 @@ gnc_ui_qif_import_commodity_update(QIFImportWindow * wind)
     }
 }
 
-static void
-_gfec_error_handler(const char *message)
-{
-    PERR("qif-import:qif-to-gnc-undo encountered an error: %s", message);
-}
-
 
 /****************************************************************
  * gnc_ui_qif_import_convert_undo
@@ -1089,7 +1082,7 @@ gnc_ui_qif_import_convert_undo(QIFImportWindow * wind)
     gnc_set_busy_cursor(NULL, TRUE);
 
     /* Undo the conversion. */
-    gfec_apply(undo, wind->imported_account_tree, _gfec_error_handler);
+    scm_call_1(undo, wind->imported_account_tree);
 
     /* There's no imported account tree any more. */
     scm_gc_unprotect_object(wind->imported_account_tree);
diff --git a/gnucash/import-export/qif-imp/qif-to-gnc.scm b/gnucash/import-export/qif-imp/qif-to-gnc.scm
index 442cb47..5f7e3f1 100644
--- a/gnucash/import-export/qif-imp/qif-to-gnc.scm
+++ b/gnucash/import-export/qif-imp/qif-to-gnc.scm
@@ -910,9 +910,7 @@
     ;; this is the grind loop.  Go over every unmarked transaction in
     ;; the candidate-xtns list.
     (let xtn-loop ((xtns candidate-xtns))
-      (if (and (and
-                (and far-acct-name near acct-name)
-                (not (qif-xtn:mark (car xtns))))
+      (if (and (not (qif-xtn:mark (car xtns)))
                (string=? (qif-xtn:from-acct (car xtns)) far-acct-name))
           (begin
             (set! how
diff --git a/gnucash/import-export/qif/CMakeLists.txt b/gnucash/import-export/qif/CMakeLists.txt
new file mode 100644
index 0000000..608e14c
--- /dev/null
+++ b/gnucash/import-export/qif/CMakeLists.txt
@@ -0,0 +1,43 @@
+
+#Tests for this directory are not run.
+add_subdirectory(test)
+
+set(qif_SOURCES
+  qif-context.c
+  qif-defaults.c
+  qif-file.c
+  qif-objects.c
+  qif-parse.c
+)
+
+# Add dependency on config.h
+set_source_files_properties (${qif_SOURCES} PROPERTIES OBJECT_DEPENDS ${CONFIG_H})
+
+set(qif_noinst_HEADERS
+  qif-file.h
+  qif-defaults.h
+  qif-import-p.h
+  qif-import.h
+  qif-objects.h
+  qif-objects-p.h
+  qif-parse.h
+)
+
+add_library(gncmod-qif ${qif_noinst_HEADERS} ${qif_SOURCES})
+
+target_link_libraries(gncmod-qif gncmod-generic-import gncmod-engine ${GLIB2_LDFLAGS})
+
+target_compile_definitions(gncmod-qif PRIVATE -DG_LOG_DOMAIN=\"gnc.import.qif\")
+
+if (APPLE)
+  set_target_properties (gncmod-qif PROPERTIES INSTALL_NAME_DIR "${CMAKE_INSTALL_FULL_LIBDIR}/gnucash")
+endif()
+
+install(TARGETS gncmod-qif
+  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/gnucash
+  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/gnucash
+  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+# No headers to install.
+
+set_local_dist(qif_DIST_local CMakeLists.txt ${qif_SOURCES} ${qif_noinst_HEADERS})
+set(qif_DIST ${qif_DIST_local} ${test_qif_DIST} PARENT_SCOPE)
diff --git a/gnucash/import-export/qif/qif-context.c b/gnucash/import-export/qif/qif-context.c
new file mode 100644
index 0000000..17fa472
--- /dev/null
+++ b/gnucash/import-export/qif/qif-context.c
@@ -0,0 +1,417 @@
+/*
+ * qif-context.c -- create/destroy QIF Contexts
+ *
+ * Written By:	Derek Atkins <derek at ihtfp.com>
+ * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+
+#include "qif-import-p.h"
+#include "qif-objects-p.h"
+
+static void qif_object_map_get_helper(gpointer key, gpointer value, gpointer listp);
+
+QifContext
+qif_context_new(void)
+{
+    QifContext ctx = g_new0(struct _QifContext, 1);
+
+    ctx->object_lists = g_hash_table_new(g_str_hash, g_str_equal);
+    ctx->object_maps = g_hash_table_new(g_str_hash, g_str_equal);
+
+    return ctx;
+}
+
+void
+qif_context_destroy(QifContext ctx)
+{
+    GList *node, *temp;
+    QifContext fctx;
+
+    if (!ctx) return;
+
+    /* First, try to destroy all the children contexts */
+    for (node = ctx->files; node; node = temp)
+    {
+        fctx = node->data;
+        temp = node->next;
+        qif_context_destroy(fctx);
+    }
+
+    /* ok, at this point we're actually destroying this context. */
+
+    /* force the end of record */
+    if (ctx->handler && ctx->handler->end)
+        ctx->handler->end(ctx);
+
+    /* destroy the state objects */
+    qif_object_list_destroy(ctx);
+    qif_object_map_destroy(ctx);
+
+    /* Remove us from our parent context */
+    if (ctx->parent)
+        ctx->parent->files = g_list_remove(ctx->parent->files, ctx);
+
+    g_free(ctx->filename);
+
+    g_assert(ctx->files == NULL);
+    g_free(ctx);
+}
+
+static GList *
+qif_context_get_foo_helper(QifContext ctx, GFunc get_helper)
+{
+    GHashTable *ht;
+    GList *node, *list = NULL;
+    QifContext fctx;
+
+    g_return_val_if_fail(ctx, NULL);
+    g_return_val_if_fail(ctx->parsed, NULL);
+    g_return_val_if_fail(get_helper, NULL);
+
+    ht = g_hash_table_new(g_direct_hash, g_direct_equal);
+
+    for (node = ctx->files; node; node = node->next)
+    {
+        fctx = node->data;
+        qif_object_list_foreach(fctx, QIF_O_TXN, get_helper, ht);
+    }
+
+    g_hash_table_foreach(ht, qif_object_map_get_helper, &list);
+    g_hash_table_destroy(ht);
+
+    return list;
+}
+
+static void
+qif_get_accts_helper(gpointer obj, gpointer htp)
+{
+    QifTxn txn = obj;
+    QifSplit split;
+    GHashTable *ht = htp;
+    GList *node;
+
+    if (txn->from_acct)
+        g_hash_table_insert(ht, txn->from_acct, txn->from_acct);
+
+    /* The default_split is using the from_acct, so we can ignore it */
+
+    for (node = txn->splits; node; node = node->next)
+    {
+        split = node->data;
+        if (split->cat.obj && split->cat_is_acct)
+            g_hash_table_insert(ht, split->cat.acct, split->cat.acct);
+    }
+}
+
+GList *
+qif_context_get_accounts(QifContext ctx)
+{
+    return qif_context_get_foo_helper(ctx, qif_get_accts_helper);
+}
+
+static void
+qif_get_cats_helper(gpointer obj, gpointer htp)
+{
+    QifTxn txn = obj;
+    QifSplit split;
+    GHashTable *ht = htp;
+    GList *node;
+
+    /* default_split uses from_acct, so no categories */
+
+    for (node = txn->splits; node; node = node->next)
+    {
+        split = node->data;
+        if (split->cat.obj && !split->cat_is_acct)
+            g_hash_table_insert(ht, split->cat.cat, split->cat.cat);
+    }
+}
+
+GList *
+qif_context_get_categories(QifContext ctx)
+{
+    return qif_context_get_foo_helper(ctx, qif_get_cats_helper);
+}
+
+/*****************************************************************************/
+
+/*
+ * Insert and remove a QifObject from the Object Maps in this Qif Context
+ */
+
+gint
+qif_object_map_count(QifContext ctx, const char *type)
+{
+    GHashTable *ht;
+
+    g_return_val_if_fail(ctx, 0);
+    g_return_val_if_fail(ctx->object_maps, 0);
+    g_return_val_if_fail(type, 0);
+
+    ht = g_hash_table_lookup(ctx->object_maps, type);
+    if (!ht)
+        return 0;
+
+    return g_hash_table_size(ht);
+}
+
+void
+qif_object_map_foreach(QifContext ctx, const char *type, GHFunc func, gpointer arg)
+{
+    GHashTable *ht;
+
+    g_return_if_fail(ctx);
+    g_return_if_fail(ctx->object_maps);
+    g_return_if_fail(type);
+
+    ht = g_hash_table_lookup(ctx->object_maps, type);
+    if (ht)
+        g_hash_table_foreach(ht, func, arg);
+}
+
+void
+qif_object_map_insert(QifContext ctx, const char *key, QifObject obj)
+{
+    GHashTable *ht;
+
+    g_return_if_fail(ctx);
+    g_return_if_fail(ctx->object_maps);
+    g_return_if_fail(key);
+    g_return_if_fail(obj);
+    g_return_if_fail(obj->type);
+
+    ht = g_hash_table_lookup(ctx->object_maps, obj->type);
+    if (!ht)
+    {
+        ht = g_hash_table_new(g_str_hash, g_str_equal);
+        g_assert(ht);
+        g_hash_table_insert(ctx->object_maps, (gpointer)obj->type, ht);
+    }
+
+    g_hash_table_insert(ht, (gpointer)key, obj);
+}
+
+void
+qif_object_map_remove(QifContext ctx, const char *type, const char *key)
+{
+    GHashTable *ht;
+
+    g_return_if_fail(ctx);
+    g_return_if_fail(ctx->object_maps);
+    g_return_if_fail(type);
+    g_return_if_fail(key);
+
+    ht = g_hash_table_lookup(ctx->object_maps, type);
+    if (!ht) return;
+
+    g_hash_table_remove(ht, key);
+}
+
+QifObject
+qif_object_map_lookup(QifContext ctx, const char *type, const char *key)
+{
+    GHashTable *ht;
+
+    g_return_val_if_fail(ctx, NULL);
+    g_return_val_if_fail(ctx->object_maps, NULL);
+    g_return_val_if_fail(type, NULL);
+    g_return_val_if_fail(key, NULL);
+
+    ht = g_hash_table_lookup(ctx->object_maps, type);
+    if (!ht) return NULL;
+
+    return g_hash_table_lookup(ht, key);
+}
+
+/* This GList _SHOULD_ be freed by the caller */
+
+static void
+qif_object_map_get_helper(gpointer key, gpointer value, gpointer arg)
+{
+    GList **listp = arg;
+    g_return_if_fail(listp);
+
+    *listp = g_list_prepend(*listp, value);
+}
+
+GList *
+qif_object_map_get(QifContext ctx, const char *type)
+{
+    GHashTable *ht;
+    GList *list = NULL;
+
+    g_return_val_if_fail(ctx, NULL);
+    g_return_val_if_fail(ctx->object_maps, NULL);
+    g_return_val_if_fail(type, NULL);
+
+    ht = g_hash_table_lookup(ctx->object_maps, type);
+    if (!ht)
+        return NULL;
+
+    g_hash_table_foreach(ht, qif_object_map_get_helper, &list);
+
+    return list;
+}
+
+static gboolean
+qif_object_map_remove_each(gpointer key, gpointer value, gpointer arg)
+{
+    QifObject obj = value;
+    obj->destroy(obj);
+    return TRUE;
+}
+
+static gboolean
+qif_object_map_remove_all(gpointer key, gpointer value, gpointer arg)
+{
+    GHashTable *ht = value;
+
+    g_hash_table_foreach_remove(ht, qif_object_map_remove_each, NULL);
+    g_hash_table_destroy(ht);
+    return TRUE;
+}
+
+void qif_object_map_destroy(QifContext ctx)
+{
+    g_return_if_fail(ctx);
+    g_return_if_fail(ctx->object_maps);
+
+    g_hash_table_foreach_remove(ctx->object_maps, qif_object_map_remove_all, NULL);
+    g_hash_table_destroy(ctx->object_maps);
+}
+
+/*****************************************************************************/
+
+/*
+ * Insert and remove a QifObject from the Object Lists in this Qif Context
+ */
+
+void
+qif_object_list_reverse(QifContext ctx, const char *type)
+{
+    GList *list;
+
+    g_return_if_fail(ctx);
+    g_return_if_fail(ctx->object_lists);
+    g_return_if_fail(type);
+
+    list = qif_object_list_get(ctx, type);
+    list = g_list_reverse(list);
+    g_hash_table_insert(ctx->object_lists, (gpointer)type, list);
+}
+
+gint
+qif_object_list_count(QifContext ctx, const char *type)
+{
+    GList *list;
+
+    g_return_val_if_fail(ctx, 0);
+    g_return_val_if_fail(ctx->object_lists, 0);
+    g_return_val_if_fail(type, 0);
+
+    list = g_hash_table_lookup(ctx->object_lists, type);
+    return g_list_length(list);
+}
+
+void
+qif_object_list_foreach(QifContext ctx, const char *type, GFunc func, gpointer arg)
+{
+    GList *list;
+
+    g_return_if_fail(ctx);
+    g_return_if_fail(ctx->object_lists);
+    g_return_if_fail(type);
+
+    list = qif_object_list_get(ctx, type);
+    g_list_foreach(list, func, arg);
+}
+
+void
+qif_object_list_insert(QifContext ctx, QifObject obj)
+{
+    GList *list;
+
+    g_return_if_fail(ctx);
+    g_return_if_fail(ctx->object_lists);
+    g_return_if_fail(obj);
+    g_return_if_fail(obj->type && *obj->type);
+
+    list = g_hash_table_lookup(ctx->object_lists, obj->type);
+    list = g_list_prepend(list, obj);
+    g_hash_table_insert(ctx->object_lists, (gpointer)obj->type, list);
+}
+
+void
+qif_object_list_remove(QifContext ctx, QifObject obj)
+{
+    GList *list;
+
+    g_return_if_fail(ctx);
+    g_return_if_fail(ctx->object_lists);
+    g_return_if_fail(obj);
+    g_return_if_fail(obj->type && *obj->type);
+
+    list = g_hash_table_lookup(ctx->object_lists, obj->type);
+    list = g_list_remove(list, obj);
+    g_hash_table_insert(ctx->object_lists, (gpointer)obj->type, list);
+}
+
+GList *
+qif_object_list_get(QifContext ctx, const char *type)
+{
+    g_return_val_if_fail(ctx, NULL);
+    g_return_val_if_fail(ctx->object_lists, NULL);
+    g_return_val_if_fail(type, NULL);
+
+    return g_hash_table_lookup(ctx->object_lists, type);
+}
+
+static gboolean
+qif_object_list_remove_all(gpointer key, gpointer value, gpointer arg)
+{
+    GList *list = value;
+    GList *node;
+    QifObject obj;
+
+    for (node = list; node; node = node->next)
+    {
+        obj = node->data;
+        obj->destroy(obj);
+    }
+
+    g_list_free(list);
+    return TRUE;
+}
+
+void
+qif_object_list_destroy(QifContext ctx)
+{
+    g_return_if_fail(ctx);
+    g_return_if_fail(ctx->object_lists);
+
+    g_hash_table_foreach_remove(ctx->object_lists, qif_object_list_remove_all, NULL);
+    g_hash_table_destroy(ctx->object_lists);
+}
diff --git a/gnucash/import-export/qif/qif-defaults.c b/gnucash/import-export/qif/qif-defaults.c
new file mode 100644
index 0000000..e29c141
--- /dev/null
+++ b/gnucash/import-export/qif/qif-defaults.c
@@ -0,0 +1,152 @@
+/*
+ * qif-defaults.c -- QIF Defaults -- default accounts...
+ *
+ * Created by:	Derek Atkins <derek at ihtfp.com>
+ * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "gnc-helpers.h"
+#include "qif-import-p.h"
+#include "qif-objects-p.h"
+#include "qif-defaults.h"
+
+
+static GList *stock_list = NULL;
+static GList *ext_stock_list = NULL;
+static GList *income_list = NULL;
+static GList *expense_list = NULL;
+static GList *equity_list = NULL;
+
+#define RETURN_ACCT(c,n,l) { if (stock_list == NULL) acct_type_init(); \
+	return find_or_make_acct(c, n, l); \
+}
+
+static void
+acct_type_init(void)
+{
+    stock_list = qif_parse_acct_type("__stock__", -1);
+    ext_stock_list = qif_parse_acct_type("__extstock__", -1);
+    income_list = qif_parse_acct_type("__income__", -1);
+    expense_list = qif_parse_acct_type("__expense__", -1);
+    equity_list = qif_parse_acct_type("__equity__", -1);
+}
+
+QifAccount qif_default_equity_acct(QifContext ctx)
+{
+    char *name = g_strdup(_("Retained Earnings"));
+    RETURN_ACCT(ctx, name, equity_list);
+}
+
+QifAccount qif_default_margin_interest_acct(QifContext ctx)
+{
+    char *name = g_strdup_printf("%s%s%s", _("Margin Interest"),
+                                 gnc_get_account_separator_string(),
+                                 ctx->current_acct->name);
+    RETURN_ACCT(ctx, name, expense_list);
+}
+
+QifAccount qif_default_commission_acct(QifContext ctx)
+{
+    char *name = g_strdup_printf("%s%s%s", _("Commissions"),
+                                 gnc_get_account_separator_string(),
+                                 ctx->current_acct->name);
+    RETURN_ACCT(ctx, name, expense_list);
+}
+
+QifAccount qif_default_stock_acct(QifContext ctx, const char *security)
+{
+    char *name = g_strdup_printf("%s%s%s", ctx->current_acct->name,
+                                 gnc_get_account_separator_string(),
+                                 security);
+    RETURN_ACCT(ctx, name, stock_list);
+}
+
+QifAccount qif_default_cglong_acct(QifContext ctx, const char *security)
+{
+    char *name = g_strdup_printf("%s%s%s%s%s", _("Cap. gain (long)"),
+                                 gnc_get_account_separator_string(),
+                                 ctx->current_acct->name,
+                                 gnc_get_account_separator_string(),
+                                 security);
+    RETURN_ACCT(ctx, name, income_list);
+}
+
+QifAccount qif_default_cgmid_acct(QifContext ctx, const char *security)
+{
+    char *name = g_strdup_printf("%s%s%s%s%s", _("Cap. gain (mid)"),
+                                 gnc_get_account_separator_string(),
+                                 ctx->current_acct->name,
+                                 gnc_get_account_separator_string(),
+                                 security);
+    RETURN_ACCT(ctx, name, income_list);
+}
+
+QifAccount qif_default_cgshort_acct(QifContext ctx, const char *security)
+{
+    char *name = g_strdup_printf("%s%s%s%s%s", _("Cap. gain (short)"),
+                                 gnc_get_account_separator_string(),
+                                 ctx->current_acct->name,
+                                 gnc_get_account_separator_string(),
+                                 security);
+    RETURN_ACCT(ctx, name, income_list);
+}
+
+QifAccount qif_default_dividend_acct(QifContext ctx, const char *security)
+{
+    char *name = g_strdup_printf("%s%s%s%s%s", _("Dividends"),
+                                 gnc_get_account_separator_string(),
+                                 ctx->current_acct->name,
+                                 gnc_get_account_separator_string(),
+                                 security);
+    RETURN_ACCT(ctx, name, income_list);
+}
+
+QifAccount qif_default_interest_acct(QifContext ctx, const char *security)
+{
+    char *name = g_strdup_printf("%s%s%s%s%s", _("Interest"),
+                                 gnc_get_account_separator_string(),
+                                 ctx->current_acct->name,
+                                 gnc_get_account_separator_string(),
+                                 security);
+    RETURN_ACCT(ctx, name, income_list);
+}
+
+QifAccount qif_default_capital_return_acct(QifContext ctx, const char *security)
+{
+    char *name = g_strdup_printf("%s%s%s%s%s", _("Cap Return"),
+                                 gnc_get_account_separator_string(),
+                                 ctx->current_acct->name,
+                                 gnc_get_account_separator_string(),
+                                 security);
+    RETURN_ACCT(ctx, name, income_list);
+}
+
+QifAccount qif_default_equity_holding(QifContext ctx, const char *security)
+{
+    return qif_default_equity_acct(ctx);
+}
+
diff --git a/gnucash/import-export/qif/qif-defaults.h b/gnucash/import-export/qif/qif-defaults.h
new file mode 100644
index 0000000..30c6562
--- /dev/null
+++ b/gnucash/import-export/qif/qif-defaults.h
@@ -0,0 +1,44 @@
+/*
+ * qif-defaults.h -- QIF Defaults -- default accounts...
+ *
+ * Created by:	Derek Atkins <derek at ihtfp.com>
+ * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ */
+
+#ifndef QIF_DEFAULTS_H
+#define QIF_DEFAULTS_H
+
+#include "qif-objects.h"
+#include "qif-import.h"
+
+QifAccount qif_default_equity_acct(QifContext ctx);
+QifAccount qif_default_equity_holding(QifContext ctx, const char *security);
+
+QifAccount qif_default_margin_interest_acct(QifContext ctx);
+QifAccount qif_default_commission_acct(QifContext ctx);
+QifAccount qif_default_stock_acct(QifContext ctx, const char *security);
+QifAccount qif_default_cglong_acct(QifContext ctx, const char *security);
+QifAccount qif_default_cgmid_acct(QifContext ctx, const char *security);
+QifAccount qif_default_cgshort_acct(QifContext ctx, const char *security);
+QifAccount qif_default_dividend_acct(QifContext ctx, const char *security);
+QifAccount qif_default_interest_acct(QifContext ctx, const char *security);
+QifAccount qif_default_capital_return_acct(QifContext ctx, const char *security);
+
+#endif /* QIF_DEFAULTS_H */
diff --git a/gnucash/import-export/qif/qif-file.c b/gnucash/import-export/qif/qif-file.c
new file mode 100644
index 0000000..775f0da
--- /dev/null
+++ b/gnucash/import-export/qif/qif-file.c
@@ -0,0 +1,326 @@
+/*
+ * qif-file.c -- parse a QIF File into its pieces
+ *
+ * Written by:  Derek Atkins  <derek@@ihtfp.com>
+ * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <string.h>
+
+#include "gnc-engine.h"
+
+#include "qif-import-p.h"
+#include "qif-objects-p.h"
+
+static QofLogModule log_module = GNC_MOD_IMPORT;
+
+
+static QifLine
+qif_make_line(const char* buf, gint lineno)
+{
+    QifLine line;
+    g_return_val_if_fail(buf && *buf, NULL);
+
+    line = g_new0(struct _QifLine, 1);
+    line->type = *buf;
+    line->lineno = lineno;
+    line->line = g_strdup(buf + 1);
+
+    return line;
+}
+
+void
+qif_record_destroy(GList *record)
+{
+    GList *node;
+    QifLine line;
+
+    for (node = record; node; node = node->next)
+    {
+        line = node->data;
+        g_free(line->line);
+        g_free(line);
+    }
+
+    g_list_free(record);
+}
+
+/* This returns a record, which is a bunch of QifLines, ending
+ * with a line with just a '^'.  If it finds a line that begins
+ * with a !, then destroy the current record state, set the "found_bangtype",
+ * and return NULL.
+ */
+static GList *
+qif_make_record(QifContext ctx, char *buf, size_t bufsiz, gboolean *found_bangtype)
+{
+    GList *record = NULL;
+    QifLine line;
+
+    g_return_val_if_fail(ctx, NULL);
+    g_return_val_if_fail(buf, NULL);
+    g_return_val_if_fail(found_bangtype, NULL);
+
+    *found_bangtype = FALSE;
+
+    while (fgets(buf, bufsiz, ctx->fp) != NULL)
+    {
+
+        /* increment the line number */
+        ctx->lineno++;
+
+        /* strip start/end whitespace */
+        g_strstrip(buf);
+
+        /* if there is nothing left in the string, ignore it */
+        if (strlen(buf) == 0)
+            continue;
+
+        /* If this is a bangline, then set the flag, clear our state, and return NULL */
+        if (*buf == '!')
+        {
+            *found_bangtype = TRUE;
+            break;
+        }
+
+        /* See if this is an End of Record marker */
+        if (*buf == '^')
+        {
+            /* Yep.  If we've got a record then break and return ... */
+            if (record)
+                break;
+            /* ... otherwise just continue reading (i.e. ignore empty records) */
+            else
+                continue;
+        }
+
+        /* otherwise, add the line to the list */
+        line = qif_make_line(buf, ctx->lineno);
+        if (line)
+            record = g_list_prepend(record, line);
+
+        /* and continue... */
+    }
+
+    /* If we found a bangtype, destroy anything we've collected */
+    if (*found_bangtype)
+    {
+        if (record)
+            PERR("error loading file: incomplete record at line %d", ctx->lineno);
+
+        qif_record_destroy(record);
+        record = NULL;
+    }
+
+    return g_list_reverse(record);
+}
+
+/* read a qif file and parse it, line by line
+ * return QIF_E_OK on success or some other QIF Error.
+ */
+static QifError
+qif_read_file(QifContext ctx, FILE *f)
+{
+    char buf[BUFSIZ];
+    GList *record;
+    gboolean found_bang;
+    QifError err = QIF_E_OK;
+
+    g_return_val_if_fail(ctx, QIF_E_BADARGS);
+    g_return_val_if_fail(f, QIF_E_BADARGS);
+
+    ctx->fp = f;
+    ctx->lineno = -1;
+
+    do
+    {
+        found_bang = FALSE;
+        record = qif_make_record(ctx, buf, sizeof(buf), &found_bang);
+
+        /* If we got a record, process it */
+        if (record)
+        {
+            if (!ctx->handler || !ctx->handler->parse_record)
+            {
+                PERR("Trying to process QIF record without a handler at %d", ctx->lineno);
+            }
+            else
+            {
+                err = ctx->handler->parse_record(ctx, record);
+            }
+
+            /* Now destroy it; we don't need it anymore */
+            qif_record_destroy(record);
+        }
+
+        /* if we found a bangtype, process that */
+        if (found_bang)
+        {
+            g_assert(*buf == '!');
+
+            /* First, process the end of the last handler.  This could possibly
+             * merge items into the context or perform some other operation
+             */
+            if (ctx->handler && ctx->handler->end)
+            {
+                err = ctx->handler->end(ctx);
+                if (err != QIF_E_OK)
+                    break;
+            }
+
+            /* Now process the bangtype (stored in buf) to set the new handler */
+            qif_parse_bangtype(ctx, buf);
+        }
+
+    }
+    while ((record || found_bang) && err == QIF_E_OK);
+
+    /* Make sure to run any end processor */
+    if (err == QIF_E_OK && ctx->handler && ctx->handler->end)
+        err = ctx->handler->end(ctx);
+
+    if (err == QIF_E_OK)
+        qif_object_list_reverse(ctx, QIF_O_TXN);
+
+    return err;
+}
+
+static QifError
+qif_import_file(QifContext ctx, const char *filename)
+{
+    QifError err;
+    FILE *fp;
+
+    g_return_val_if_fail(ctx, QIF_E_BADARGS);
+    g_return_val_if_fail(filename, QIF_E_BADARGS);
+    g_return_val_if_fail(*filename, QIF_E_BADARGS);
+
+    /* Open the file */
+    fp = g_fopen(filename, "r");
+    if (fp == NULL)
+        return QIF_E_NOFILE;
+
+    ctx->filename = g_strdup(filename);
+
+    /* read the file */
+    err = qif_read_file(ctx, fp);
+
+    /* close the file */
+    fclose(fp);
+
+    return err;
+}
+
+
+QifContext
+qif_file_new(QifContext ctx, const char *filename)
+{
+    QifContext fctx;
+
+    g_return_val_if_fail(ctx, NULL);
+    g_return_val_if_fail(filename, NULL);
+
+    fctx = qif_context_new();
+
+    /* we should assume that we've got a bank account... just in case.. */
+    qif_parse_bangtype(fctx, "!type:bank");
+
+    /* Open the file */
+    if (qif_import_file(fctx, filename) != QIF_E_OK)
+    {
+        qif_context_destroy(fctx);
+        fctx = NULL;
+    }
+
+    /* Return the new context */
+    if (fctx)
+    {
+        ctx->files = g_list_prepend(ctx->files, fctx);
+        fctx->parent = ctx;
+
+        /* Make sure the file gets merged into the parent */
+        ctx->parsed = FALSE;
+    }
+
+    return fctx;
+}
+
+QifError
+qif_file_parse(QifContext ctx, gpointer ui_args)
+{
+    g_return_val_if_fail(ctx, QIF_E_BADARGS);
+    g_return_val_if_fail(!qif_file_needs_account(ctx), QIF_E_BADSTATE);
+
+    qif_parse_all(ctx, ui_args);
+    ctx->parsed = TRUE;
+
+    return QIF_E_OK;
+}
+
+gboolean
+qif_file_needs_account(QifContext ctx)
+{
+    g_return_val_if_fail(ctx, FALSE);
+
+    return ((ctx->parse_flags & QIF_F_TXN_NEEDS_ACCT) ||
+            (ctx->parse_flags & QIF_F_ITXN_NEEDS_ACCT));
+}
+
+const char *
+qif_file_filename(QifContext ctx)
+{
+    g_return_val_if_fail(ctx, NULL);
+    return ctx->filename;
+}
+
+static void
+set_txn_acct(gpointer obj, gpointer arg)
+{
+    QifTxn txn = obj;
+    QifAccount acct = arg;
+
+    if (!txn->from_acct)
+        txn->from_acct = acct;
+}
+
+void
+qif_file_set_default_account(QifContext ctx, const char *acct_name)
+{
+    QifAccount acct;
+
+    g_return_if_fail(ctx);
+    g_return_if_fail(acct_name);
+
+    if (! qif_file_needs_account(ctx)) return;
+
+    acct = find_or_make_acct(ctx, g_strdup(acct_name),
+                             qif_parse_acct_type_guess(ctx->parse_type));
+
+    qif_object_list_foreach(ctx, QIF_O_TXN, set_txn_acct, acct);
+
+    qif_clear_flag(ctx->parse_flags, QIF_F_TXN_NEEDS_ACCT);
+    qif_clear_flag(ctx->parse_flags, QIF_F_ITXN_NEEDS_ACCT);
+}
diff --git a/gnucash/import-export/qif/qif-file.h b/gnucash/import-export/qif/qif-file.h
new file mode 100644
index 0000000..c4b85f1
--- /dev/null
+++ b/gnucash/import-export/qif/qif-file.h
@@ -0,0 +1,36 @@
+/* qif-import-p.h -- a QIF Importer module (private headers)
+ *
+ * Written By:	Derek Atkins <derek at ihtfp.com>
+ * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ */
+
+#ifndef QIF_FILE_H
+#define QIF_FILE_H
+
+struct _QifLine
+{
+    char		type;
+    gint		lineno;
+    char *	line;
+};
+
+void qif_record_destroy(GList *record);
+
+#endif /* QIF_FILE_H */
diff --git a/gnucash/import-export/qif/qif-import-p.h b/gnucash/import-export/qif/qif-import-p.h
new file mode 100644
index 0000000..96c6314
--- /dev/null
+++ b/gnucash/import-export/qif/qif-import-p.h
@@ -0,0 +1,100 @@
+/* qif-import-p.h -- a QIF Importer module (private headers)
+ *
+ * Written By:	Derek Atkins <derek at ihtfp.com>
+ * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ */
+
+#ifndef QIF_IMPORT_P_H
+#define QIF_IMPORT_P_H
+
+#include "qif-import.h"
+#include "qif-objects.h"
+#include "qif-parse.h"
+#include "qif-file.h"
+
+#include <stdio.h>
+
+struct _QifHandler
+{
+    void		(*init)(QifContext ctx);
+    QifError	(*parse_record)(QifContext ctx, GList *record);
+    QifError	(*end)(QifContext ctx);
+};
+
+struct _QifContext
+{
+    /* The parent context */
+    QifContext	parent;
+
+    /* file information */
+    char *	filename;
+    FILE *	fp;
+    gint		lineno;
+
+    /* This describes what we are parsing right now */
+    QifType	parse_type;
+    QifHandler	handler;
+    gpointer	parse_state;
+
+    /* A bunch of flags for the current handler */
+    gint		parse_flags;
+    gboolean	parsed;
+
+    /* The current and "opening balance" account */
+    QifAccount	current_acct;
+    QifAccount	opening_bal_acct;
+
+    /* HashTable of Maps of data objects */
+    GHashTable *	object_maps;
+
+    /* HashTable of Lists of data objects */
+    GHashTable *	object_lists;
+
+    /* List of files */
+    GList *files;
+};
+
+/* Object Maps */
+gint qif_object_map_count(QifContext ctx, const char *type);
+void qif_object_map_foreach(QifContext ctx, const char *type,
+                            GHFunc func, gpointer arg);
+void qif_object_map_insert(QifContext ctx, const char *key, QifObject obj);
+void qif_object_map_remove(QifContext ctx, const char *type, const char *key);
+QifObject qif_object_map_lookup(QifContext ctx, const char *type, const char *key);
+void qif_object_map_destroy(QifContext ctx);
+/* GList _SHOULD_ be freed by the caller */
+GList * qif_object_map_get(QifContext ctx, const char *type);
+
+/* Object Lists */
+void qif_object_list_reverse(QifContext ctx, const char *type);
+gint qif_object_list_count(QifContext ctx, const char *type);
+void qif_object_list_foreach(QifContext ctx, const char *type,
+                             GFunc func, gpointer arg);
+void qif_object_list_insert(QifContext ctx, QifObject obj);
+void qif_object_list_remove(QifContext ctx, QifObject obj);
+void qif_object_list_destroy(QifContext ctx);
+/* GList should NOT be freed by the caller */
+GList *qif_object_list_get(QifContext ctx, const char *type);
+
+/* Set and clear flags in bit-flags */
+#define qif_set_flag(i,f) (i |= f)
+#define qif_clear_flag(i,f) (i &= ~f)
+
+#endif /* QIF_IMPORT_P_H */
diff --git a/gnucash/import-export/qif/qif-import.h b/gnucash/import-export/qif/qif-import.h
new file mode 100644
index 0000000..9602029
--- /dev/null
+++ b/gnucash/import-export/qif/qif-import.h
@@ -0,0 +1,159 @@
+/*
+ * qif-import.h -- a QIF Import module
+ *
+ * Written By:	Derek Atkins <derek at ihtfp.com>
+ * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ */
+
+#ifndef QIF_IMPORT_H
+#define QIF_IMPORT_H
+
+#include <stdio.h>
+#include "qof.h"
+
+typedef enum
+{
+    QIF_TYPE_BANK = 1,
+    QIF_TYPE_CASH,
+    QIF_TYPE_CCARD,
+    QIF_TYPE_INVST,
+    QIF_TYPE_PORT,
+    QIF_TYPE_OTH_A,
+    QIF_TYPE_OTH_L,
+    QIF_TYPE_CLASS,
+    QIF_TYPE_CAT,
+    QIF_TYPE_SECURITY,
+    QIF_ACCOUNT,
+    QIF_AUTOSWITCH,
+    QIF_CLEAR_AUTOSWITCH
+} QifType;
+
+/* Make sure this patches */
+#define QIF_TYPE_MAX QIF_CLEAR_AUTOSWITCH
+
+typedef struct _QifHandler *QifHandler;
+typedef struct _QifContext *QifContext;
+typedef struct _QifLine *QifLine;
+
+/* Qif Flags */
+#define QIF_F_IGNORE_ACCOUNTS	(1 << 0)
+#define QIF_F_TXN_NEEDS_ACCT	(1 << 1)
+#define QIF_F_ITXN_NEEDS_ACCT	(1 << 2)
+
+/* Qif Reconciled Flag */
+typedef enum
+{
+    QIF_R_NO = 0,
+    QIF_R_CLEARED,
+    QIF_R_RECONCILED,
+    QIF_R_BUDGETED,
+} QifRecnFlag;
+
+/* Qif Errors */
+
+typedef enum
+{
+    QIF_E_OK = 0,
+    QIF_E_INTERNAL,
+    QIF_E_BADSTATE,
+    QIF_E_BADARGS,
+    QIF_E_NOFILE,
+} QifError;
+
+
+/* Qif (investment?) Actions */
+typedef enum
+{
+    QIF_A_NONE = 0,
+    QIF_A_BUY,
+    QIF_A_BUYX,
+    QIF_A_CGLONG,
+    QIF_A_CGLONGX,
+    QIF_A_CGMID,
+    QIF_A_CGMIDX,
+    QIF_A_CGSHORT,
+    QIF_A_CGSHORTX,
+    QIF_A_DIV,
+    QIF_A_DIVX,
+    QIF_A_EXERCISE,
+    QIF_A_EXERCISEX,
+    QIF_A_EXPIRE,
+    QIF_A_GRANT,
+    QIF_A_INTINC,
+    QIF_A_INTINCX,
+    QIF_A_MARGINT,
+    QIF_A_MARGINTX,
+    QIF_A_MISCEXP,
+    QIF_A_MISCEXPX,
+    QIF_A_MISCINC,
+    QIF_A_MISCINCX,
+    QIF_A_REINVDIV,
+    QIF_A_REINVINT,
+    QIF_A_REINVLG,
+    QIF_A_REINVMD,
+    QIF_A_REINVSG,
+    QIF_A_REINVSH,
+    QIF_A_REMINDER,
+    QIF_A_RTRNCAP,
+    QIF_A_RTRNCAPX,
+    QIF_A_SELL,
+    QIF_A_SELLX,
+    QIF_A_SHRSIN,
+    QIF_A_SHRSOUT,
+    QIF_A_STKSPLIT,
+    QIF_A_VEST,
+    QIF_A_XIN,
+    QIF_A_XOUT,
+} QifAction;
+
+/* Public API Functions */
+
+/* Create a QIF Import Context */
+QifContext qif_context_new(void);
+void qif_context_destroy(QifContext ctx);
+
+/* Open and read a QIF File.  You must pass in the parent
+ * context; it will return the child (file) context
+ */
+QifContext qif_file_new(QifContext ctx, const char* filename);
+
+/* Does a qif-file need a default QIF account? */
+gboolean qif_file_needs_account(QifContext ctx);
+
+/* Return the filename of the QIF file */
+const char * qif_file_filename(QifContext ctx);
+
+/* Provide a default QIF Account for the QIF File */
+void qif_file_set_default_account(QifContext ctx, const char *acct_name);
+
+/* Parse the QIF File */
+QifError qif_file_parse(QifContext ctx, gpointer ui_arg);
+
+/* Merge all the qif-files from the children and into the context */
+void qif_parse_merge_files(QifContext ctx);
+
+/* Obtain the list of USED QifAccounts and QifCategories.  Finds all
+ * references from the transactions in the QifContext.  The returned
+ * GList must be freed by the caller.
+ */
+GList *qif_context_get_accounts(QifContext ctx);
+GList *qif_context_get_categories(QifContext ctx);
+
+#endif /* QIF_IMPORT_H */
diff --git a/gnucash/import-export/qif/qif-objects-p.h b/gnucash/import-export/qif/qif-objects-p.h
new file mode 100644
index 0000000..dbd3aa4
--- /dev/null
+++ b/gnucash/import-export/qif/qif-objects-p.h
@@ -0,0 +1,174 @@
+/*
+ * qif-objects-p.h -- Private header: QIF objects for the QIF importer
+ *
+ * Written By:	Derek Atkins <derek at ihtfp.com>
+ * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ */
+
+#ifndef QIF_OBJECTS_P_H
+#define QIF_OBJECTS_P_H
+
+#include "qof.h"
+
+#include "qif-import.h"
+#include "qif-objects.h"
+
+struct _QifAccount
+{
+    struct _QifObject obj;
+
+    char *	name;
+    char *	desc;
+
+    char *	limitstr;
+    gnc_numeric	limit;
+
+    char *	budgetstr;
+    gnc_numeric	budget;
+
+    GList *	type_list;
+
+};
+
+struct _QifCategory
+{
+    struct _QifObject obj;
+
+    char *	name;
+    char *	desc;
+    char *	taxclass;
+
+    gboolean	taxable;
+    gboolean	expense;
+    gboolean	income;
+
+    char *	budgetstr;
+    gnc_numeric	budget;
+
+};
+
+struct _QifClass
+{
+    struct _QifObject obj;
+
+    char *	name;
+    char *	desc;
+    char *	taxdesig;
+
+};
+
+struct _QifSecurity
+{
+    struct _QifObject obj;
+
+    char *	name;
+    char *	symbol;
+    char *	type;
+
+};
+
+struct _QifTxn
+{
+    struct _QifObject obj;
+
+    QifType	txn_type;
+
+    char *	datestr;
+    Timespec	date;
+
+    char *	payee;
+    char *	address;
+    char *	num;
+
+    QifRecnFlag	cleared;
+
+    /* Investment info */
+    QifInvstTxn	invst_info;
+
+    /* The default_split is the default (forward) part of the QIF transaction */
+    QifSplit	default_split;
+
+    /* The current_split (if any) defines the current "qif split" we are handling */
+    QifSplit	current_split;
+
+    /* The "from" account */
+    QifAccount	from_acct;
+
+    /* The list of splits for this txn */
+    GList *	splits;
+
+};
+
+struct _QifSplit
+{
+    char *	memo;
+
+    char *	amountstr;
+    gnc_numeric	amount;
+    gnc_numeric	value;
+
+    char *	catstr;
+
+    /* parsed category/account info */
+
+    union
+    {
+        QifObject	obj;
+        QifCategory	cat;
+        QifAccount	acct;
+    } cat;
+    gboolean	cat_is_acct;
+    QifClass	cat_class;
+
+};
+
+struct _QifInvstTxn
+{
+    QifAction	action;
+
+    gnc_numeric	amount;
+    gnc_numeric	d_amount;
+    gnc_numeric	price;
+    gnc_numeric	shares;
+    gnc_numeric	commission;
+
+    char *	amountstr;
+    char *	d_amountstr;
+    char *	pricestr;
+    char *	sharesstr;
+    char *	commissionstr;
+
+    char *	security;
+    char *	catstr;
+
+    union
+    {
+        QifObject	obj;
+        QifCategory	cat;
+        QifAccount	acct;
+    } far_cat;
+    gboolean	far_cat_is_acct;
+};
+
+/* to be run after parsing all the dates and amounts */
+void qif_txn_setup_splits(QifTxn txn);
+void qif_invst_txn_setup_splits(QifContext ctx, QifTxn txn);
+
+#endif /* QIF_OBJECTS_P_H */
diff --git a/gnucash/import-export/qif/qif-objects.c b/gnucash/import-export/qif/qif-objects.c
new file mode 100644
index 0000000..0fad928
--- /dev/null
+++ b/gnucash/import-export/qif/qif-objects.c
@@ -0,0 +1,1468 @@
+/*
+ * qif-objects.c -- Objects for the QIF Importer
+ *
+ * Written by:	Derek Atkins  <derek at ihtfp.com>
+ * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <string.h>
+#include "Account.h"
+
+#include "gnc-engine.h"
+
+#include "qif-import-p.h"
+#include "qif-objects-p.h"
+#include "qif-defaults.h"
+
+static QofLogModule log_module = GNC_MOD_IMPORT;
+
+/* create a new object of type t, with type-string type and
+ * destroy function dest.  Requires 'obj' to be set.
+ */
+#define qif_object_new(t,typ,dest) ({ \
+	obj = (QifObject) g_new0(t, 1); \
+	obj->type = typ; \
+	obj->destroy = dest; \
+	obj; \
+})
+
+/* Save the string from this "line".  Also:
+ * - make sure we're not over-writing anything.
+ * - make sure the 'line' object no longer references the string.
+ */
+#define qif_save_str(var) { \
+	if (var) { \
+		PERR("duplicate found at line %d: %s", line->lineno, line->line); \
+		g_free(var); \
+	} \
+	(var) = line->line; \
+	line->line = NULL; \
+}
+
+/* QIF Account */
+static void
+qif_account_destroy(QifObject obj)
+{
+    QifAccount acct = (QifAccount) obj;
+
+    g_free(acct->name);
+    g_free(acct->desc);
+    g_free(acct->limitstr);
+    g_free(acct->budgetstr);
+
+    g_free(acct);
+};
+
+static QifAccount
+qif_account_new(void)
+{
+    QifObject obj;
+    QifAccount acct;
+
+    obj = qif_object_new(struct _QifAccount, QIF_O_ACCOUNT, qif_account_destroy);
+
+    acct = (QifAccount)obj;
+    acct->type_list = qif_parse_acct_type("bank", -1);
+
+    acct->limit = gnc_numeric_zero();
+    acct->budget = gnc_numeric_zero();
+    return acct;
+}
+
+/*
+ * Merge acct into ctx.  If this account already exists in ctx then
+ * merge in any new values from acct into the ctx version and return
+ * the existing acct.  If the account does not already exist, then
+ * insert it into the ctx and return it.
+ */
+QifAccount
+qif_account_merge(QifContext ctx, QifAccount acct)
+{
+    QifAccount acct2 =
+        (QifAccount)qif_object_map_lookup(ctx, acct->obj.type, acct->name);
+
+    if (!acct2)
+    {
+        qif_object_map_insert(ctx, acct->obj.type, (QifObject)acct);
+        return acct;
+    }
+
+    /* obviously the name is the same, so don't worry about that */
+
+    if (!acct2->desc && acct->desc)
+        acct2->desc = g_strdup(acct->desc);
+
+    if (!acct2->type_list && acct->type_list)
+        acct2->type_list = acct->type_list;
+
+    if (!acct2->limitstr && acct->limitstr)
+    {
+        acct2->limitstr = g_strdup(acct->limitstr);
+        acct2->limit = acct->limit;
+    }
+
+    if (!acct2->budgetstr && acct->budgetstr)
+    {
+        acct2->budgetstr = g_strdup(acct->budgetstr);
+        acct2->budget = acct->budget;
+    }
+
+    return acct2;
+}
+
+static QifError
+qif_account_parse(QifContext ctx, GList *record)
+{
+    QifAccount acct, temp;
+    QifLine line;
+
+    g_return_val_if_fail(ctx, QIF_E_INTERNAL);
+    g_return_val_if_fail(record, QIF_E_BADSTATE);
+
+    acct = qif_account_new();
+
+    for (; record; record = record->next)
+    {
+        line = record->data;
+
+        switch (line->type)
+        {
+        case 'N':			/* N : account name */
+            qif_save_str(acct->name);
+            break;
+        case 'D': 			/* D : account description */
+            qif_save_str(acct->desc);
+            break;
+        case 'T':			/* T : account type */
+            acct->type_list = qif_parse_acct_type(line->line, line->lineno);
+            break;
+        case 'L':			/* L : account limit */
+            qif_save_str(acct->limitstr);
+            break;
+        case 'B':			/* B : account budget */
+            qif_save_str(acct->budgetstr);
+            break;
+        default:
+            PERR("Unknown QIF account data at line %d: %s", line->lineno, line->line);
+        }
+    }
+
+    /* Merge the account into the context */
+    temp = qif_account_merge(ctx, acct);
+    if (! (ctx->parse_flags & QIF_F_IGNORE_ACCOUNTS))
+        ctx->current_acct = temp;
+    if (temp != acct)
+        qif_account_destroy((QifObject)acct);
+
+    return QIF_E_OK;
+}
+
+/* QIF Category */
+static void
+qif_cat_destroy(QifObject obj)
+{
+    QifCategory cat = (QifCategory) obj;
+
+    g_free(cat->name);
+    g_free(cat->desc);
+    g_free(cat->taxclass);
+    g_free(cat->budgetstr);
+
+    g_free(cat);
+}
+
+static QifCategory
+qif_cat_new(void)
+{
+    QifObject obj;
+    QifCategory cat;
+
+    obj = qif_object_new(struct _QifCategory, QIF_O_CATEGORY, qif_cat_destroy);
+    cat = (QifCategory)obj;
+    cat->budget = gnc_numeric_zero();
+
+    return cat;
+}
+
+/*
+ * Merge cat into ctx.  If this category already exists in ctx then
+ * merge in any new values from cat into the ctx version and return
+ * the existing cat.  If the category does not already exist, then
+ * insert it into the ctx and return it.
+ */
+QifCategory
+qif_cat_merge(QifContext ctx, QifCategory cat)
+{
+    QifCategory cat2 =
+        (QifCategory)qif_object_map_lookup(ctx, cat->obj.type, cat->name);
+
+    if (!cat2)
+    {
+        qif_object_map_insert(ctx, cat->obj.type, (QifObject)cat);
+        return cat;
+    }
+
+    /* obviously the name is the same, so don't worry about that */
+
+    if (!cat2->desc && cat->desc)
+        cat2->desc = g_strdup(cat->desc);
+
+    if (!cat2->taxclass && cat->taxclass)
+        cat2->taxclass = g_strdup(cat->taxclass);
+
+    cat2->taxable = (cat2->taxable || cat->taxable);
+    cat2->expense = (cat2->expense || cat->expense);
+    cat2->income = (cat2->income || cat->income);
+
+    if (!cat2->budgetstr && cat->budgetstr)
+    {
+        cat2->budgetstr = g_strdup(cat->budgetstr);
+        cat2->budget = cat->budget;
+    }
+
+    return cat2;
+}
+
+static QifError
+qif_cat_parse(QifContext ctx, GList *record)
+{
+    QifCategory cat;
+    QifLine line;
+
+    g_return_val_if_fail(ctx, QIF_E_INTERNAL);
+    g_return_val_if_fail(record, QIF_E_BADSTATE);
+
+    cat = qif_cat_new();
+
+    for (; record; record = record->next)
+    {
+        line = record->data;
+
+        switch (line->type)
+        {
+        case 'N':			/* N : category name */
+            qif_save_str(cat->name);
+            break;
+        case 'D':			/* D : category description */
+            qif_save_str(cat->desc);
+            break;
+        case 'T':			/* T : category is taxable? */
+            cat->taxable = TRUE;
+            break;
+        case 'E':			/* E : category is expense? */
+            cat->expense = TRUE;
+            break;
+        case 'I':			/* I : category is income? */
+            cat->income = TRUE;
+            break;
+        case 'R':			/* R : category taxclass XXX */
+            /* XXX: a number? */
+            qif_save_str(cat->taxclass);
+            break;
+        case 'B':			/* B : category budget */
+            qif_save_str(cat->budgetstr);
+            break;
+        default:
+            PERR("Unknown QIF category data at line %d: %s", line->lineno, line->line);
+        }
+    }
+
+    if (qif_cat_merge(ctx, cat) != cat)
+        qif_cat_destroy((QifObject)cat);
+
+    return QIF_E_OK;
+}
+
+/* QIF Class */
+static void
+qif_class_destroy(QifObject obj)
+{
+    QifClass qclass = (QifClass) obj;
+
+    g_free(qclass->name);
+    g_free(qclass->desc);
+    g_free(qclass->taxdesig);
+
+    g_free(qclass);
+}
+
+static QifClass
+qif_class_new()
+{
+    QifObject obj;
+
+    obj = qif_object_new(struct _QifClass, QIF_O_CLASS, qif_class_destroy);
+    return (QifClass)obj;
+}
+
+/*
+ * Merge qclass into ctx.  If this class already exists in ctx then
+ * merge in any new values from qclass into the ctx version and return
+ * the existing qclass.  If the class does not already exist, then
+ * insert it into the ctx and return it.
+ */
+QifClass
+qif_class_merge(QifContext ctx, QifClass qclass)
+{
+    QifClass qclass2 =
+        (QifClass)qif_object_map_lookup(ctx, qclass->obj.type, qclass->name);
+
+    if (!qclass2)
+    {
+        qif_object_map_insert(ctx, qclass->obj.type, (QifObject)qclass);
+        return qclass;
+    }
+
+    /* obviously the name is the same, so don't worry about that */
+
+    if (!qclass2->desc && qclass->desc)
+        qclass2->desc = g_strdup(qclass->desc);
+
+    if (!qclass2->taxdesig && qclass->taxdesig)
+        qclass2->taxdesig = g_strdup(qclass->taxdesig);
+
+    return qclass2;
+}
+
+static QifError
+qif_class_parse(QifContext ctx, GList *record)
+{
+    QifClass qclass;
+    QifLine line;
+
+    g_return_val_if_fail(ctx, QIF_E_INTERNAL);
+    g_return_val_if_fail(record, QIF_E_BADSTATE);
+
+    qclass = qif_class_new();
+
+    for (; record; record = record->next)
+    {
+        line = record->data;
+
+        switch (line->type)
+        {
+        case 'N':			/* N : class name */
+            qif_save_str(qclass->name);
+            break;
+        case 'D':			/* D : class description */
+            qif_save_str(qclass->desc);
+            break;
+        case 'R':			/* R : Tax designator */
+            qif_save_str(qclass->taxdesig);
+            break;
+        default:
+            PERR("Unknown QIF class data at line %d: %s", line->lineno, line->line);
+        }
+    }
+
+    if (qif_class_merge(ctx, qclass) != qclass)
+        qif_class_destroy((QifObject)qclass);
+
+    return QIF_E_OK;
+}
+
+/* QIF Security Symbol */
+static void
+qif_security_destroy(QifObject obj)
+{
+    QifSecurity security = (QifSecurity) obj;
+
+    g_free(security->name);
+    g_free(security->symbol);
+    g_free(security->type);
+
+    g_free(security);
+}
+
+static QifSecurity
+qif_security_new()
+{
+    QifObject obj;
+
+    obj = qif_object_new(struct _QifSecurity, QIF_O_SECURITY, qif_security_destroy);
+    return (QifSecurity)obj;
+}
+
+/*
+ * Merge security into ctx.  If this security already exists in ctx then
+ * merge in any new values from security into the ctx version and return
+ * the existing security.  If the security does not already exist, then
+ * insert it into the ctx and return it.
+ */
+QifSecurity
+qif_security_merge(QifContext ctx, QifSecurity security)
+{
+    QifSecurity security2 =
+        (QifSecurity)qif_object_map_lookup(ctx, security->obj.type, security->name);
+
+    if (!security2)
+    {
+        qif_object_map_insert(ctx, security->obj.type, (QifObject)security);
+        return security;
+    }
+
+    /* obviously the name is the same, so don't worry about that */
+
+    if (!security2->symbol && security->symbol)
+        security2->symbol = g_strdup(security->symbol);
+
+    if (!security2->type && security->type)
+        security2->type = g_strdup(security->type);
+
+    return security2;
+}
+
+static QifError
+qif_security_parse(QifContext ctx, GList *record)
+{
+    QifSecurity security;
+    QifLine line;
+
+    g_return_val_if_fail(ctx, QIF_E_INTERNAL);
+    g_return_val_if_fail(record, QIF_E_BADSTATE);
+
+    security = qif_security_new();
+
+    for (; record; record = record->next)
+    {
+        line = record->data;
+
+        switch (line->type)
+        {
+        case 'N':			/* N : security name */
+            qif_save_str(security->name);
+            break;
+        case 'S':			/* S : security symbol */
+            qif_save_str(security->symbol);
+            break;
+        case 'T':			/* T : security type */
+            qif_save_str(security->type);
+            break;
+        default:
+            PERR("Unknown QIF security data at line %d: %s", line->lineno, line->line);
+        }
+    }
+
+    if (qif_security_merge(ctx, security) != security)
+        qif_security_destroy((QifObject)security);
+
+    return QIF_E_OK;
+}
+
+/********************* TXN *********************/
+
+static QifSplit
+qif_split_new()
+{
+    QifSplit split = g_new0(struct _QifSplit, 1);
+
+    /* Initialize to 'zero' (even though they are not valid) */
+    split->amount = gnc_numeric_zero();
+    split->value = gnc_numeric_zero();
+
+    return split;
+}
+
+static void
+qif_split_destroy(QifSplit split)
+{
+    if (!split) return;
+
+    g_free(split->memo);
+    g_free(split->catstr);
+    g_free(split->amountstr);
+
+    g_free(split);
+}
+
+static QifSplit
+qif_split_copy(QifSplit split)
+{
+    QifSplit s = qif_split_new();
+
+    memcpy(s, split, sizeof(*s));
+    if (s->memo) s->memo = g_strdup(s->memo);
+    if (s->amountstr) s->amountstr = g_strdup(s->amountstr);
+    if (s->catstr) s->memo = g_strdup(s->catstr);
+
+    return s;
+}
+
+/* Forward declarations */
+static void qif_txn_invst_destroy(QifInvstTxn);
+
+/* QIF Transaction */
+
+static void
+qif_split_parse_category(QifContext ctx, QifSplit split)
+{
+    char *cat = NULL;
+    char *cat_class = NULL;
+    char *miscx_cat = NULL;
+    char *miscx_class = NULL;
+
+    gboolean miscx_is_acct;
+
+    static GList *types = NULL;
+
+    g_return_if_fail(ctx);
+    g_return_if_fail(split);
+    g_return_if_fail(split->cat.cat == NULL && split->cat_class == NULL);
+
+    if (qif_parse_split_category(split->catstr,
+                                 &cat, &split->cat_is_acct, &cat_class,
+                                 &miscx_cat, &miscx_is_acct, &miscx_class))
+    {
+        g_assert(cat);
+
+        if (split->cat_is_acct)
+        {
+            if (types == NULL)
+                types = qif_parse_acct_type("__any_bank__", -1);
+
+            split->cat.acct = find_or_make_acct(ctx, cat, types);
+
+        }
+        else
+            split->cat.cat = find_or_make_cat(ctx, cat);
+
+        if (cat_class)
+            split->cat_class = find_or_make_class(ctx, cat_class);
+
+        /* miscx isn't used in a normal transaction, so just ignore it */
+        if (miscx_cat)
+            g_free(miscx_cat);
+        if (miscx_class)
+            g_free(miscx_class);
+
+    }
+    else
+        PERR("Problem parsing split category: %s", split->catstr);
+}
+
+static void
+qif_txn_destroy(QifObject obj)
+{
+    QifTxn txn = (QifTxn) obj;
+    GList *node;
+    QifSplit split;
+
+    g_free(txn->datestr);
+    g_free(txn->payee);
+    g_free(txn->address);
+    g_free(txn->num);
+
+    if (txn->invst_info)
+        qif_txn_invst_destroy(txn->invst_info);
+
+    for (node = txn->splits; node; node = node->next)
+    {
+        split = node->data;
+        if (split == txn->default_split)
+            txn->default_split = NULL;
+        if (split == txn->current_split)
+            txn->current_split = NULL;
+
+        qif_split_destroy(split);
+    }
+
+    g_list_free(txn->splits);
+    qif_split_destroy(txn->default_split);
+    qif_split_destroy(txn->current_split);
+
+    g_free(txn);
+}
+
+static QifTxn
+qif_txn_new(void)
+{
+    QifObject obj;
+    QifTxn txn;
+
+    obj = qif_object_new(struct _QifTxn, "qif-txn", qif_txn_destroy);
+    txn = (QifTxn) obj;
+    txn->default_split = qif_split_new();
+
+    return txn;
+}
+
+static void
+qif_txn_init(QifContext ctx)
+{
+    qif_clear_flag(ctx->parse_flags, QIF_F_IGNORE_ACCOUNTS);
+    ctx->parse_state = NULL;
+}
+
+static gboolean
+qif_is_bad_numeric_string(const char* line)
+{
+    return (strncmp(line, "...", 3) == 0);
+}
+
+/*
+ * this is called for the first transaction after each !Type: tag.
+ *
+ * if the first transaction after a !Type: tag has a payee of "Opening
+ * Balance" or "Initial Balance", we have to massage the transaction a
+ * little.  The meaning of an OB transaction is "transfer from Equity
+ * to the account specified in the L line."  Idiomatically, ms-money
+ * and some others use this transaction instead of an Account record
+ * to specify "this" account (the from-account for all following
+ * transactions), so we have to allow for that.
+ *
+ * Even if the payee isn't "Opening Balance", we if we have no default
+ * from-account by this time we need to set one.  In that case we set
+ * the default account based on the file name.
+ *
+ * If we DO know the account already, and this is a transfer to it,
+ * it's also an opening balance regardless of the payee.
+ *
+ * In the end make sure that the context 'current account' is set.
+ */
+static void
+qif_process_opening_balance_txn(QifContext ctx, QifTxn txn)
+{
+    QifSplit split = txn->default_split;
+    QifAccount cur_acct = NULL;	/* We know that ctx->current_acct is NULL */
+
+    g_return_if_fail(txn->invst_info == NULL);
+
+    if ((!cur_acct && txn->payee &&
+            (!strcasecmp(txn->payee, "Opening Balance") ||
+             !strcasecmp(txn->payee, "Initial Balance")) && split->cat_is_acct) ||
+            (cur_acct &&
+             ((split->cat_is_acct && !strcasecmp(split->cat.acct->name, cur_acct->name))
+              ||
+              (!split->cat_is_acct && !strcasecmp(split->cat.cat->name, cur_acct->name))))
+       )
+    {
+
+        /* This is an explicit "Opening Balance" transactions.  We need to
+         * change the "from account" to point to the equity account that
+         * the opening balance comes from...
+         */
+        if (split->cat_is_acct)
+            cur_acct = split->cat.acct;
+        else
+        {
+            g_assert(split->cat.cat);
+            cur_acct = find_or_make_acct(ctx, g_strdup(split->cat.cat->name),
+                                         qif_parse_acct_type_guess(txn->txn_type));
+            split->cat_is_acct = TRUE;
+        }
+        split->cat.acct = qif_default_equity_acct(ctx);
+    }
+
+    /*
+     * If we found an opening balance account then set up the context.
+     * If we didn't actually succeed in finding an account then
+     * set a flag so we can go back later and look for it.
+     */
+
+    if (cur_acct)
+    {
+        ctx->opening_bal_acct = cur_acct;
+        ctx->current_acct = cur_acct;
+    }
+    else
+        qif_set_flag(ctx->parse_flags, QIF_F_TXN_NEEDS_ACCT);
+}
+
+/* process all the splits in the transaction -- if this is a "split
+ * transaction" then make sure the sum of all the amounts (including
+ * the default split) does NOT equal zero -- if it does then we want
+ * to reverse all the splits.  The "amount" should be the 'T' amount
+ * from the txn.
+ */
+static void
+qif_txn_fix_amounts(QifTxn txn, gnc_numeric amount)
+{
+    gnc_numeric sum = amount;
+    QifSplit split;
+    GList *node;
+
+    g_return_if_fail(txn);
+
+    /* No current_split, so this is NOT a split transaction. */
+    if (!txn->current_split) return;
+
+    /* Then add in every split in the split-list */
+    for (node = txn->splits; node; node = node->next)
+    {
+        split = node->data;
+        sum = gnc_numeric_add(sum, split->amount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
+    }
+
+    /* if the sum is not zero then reverse all the amounts in the split list */
+    if (!gnc_numeric_zero_p(sum))
+        for (node = txn->splits; node; node = node->next)
+        {
+            split = node->data;
+            split->amount = gnc_numeric_neg(split->amount);
+        }
+}
+
+static QifError
+qif_txn_parse(QifContext ctx, GList *record)
+{
+    QifTxn txn;
+    QifLine line;
+    GList *node;
+    QifSplit split;
+
+    g_return_val_if_fail(ctx, QIF_E_INTERNAL);
+    g_return_val_if_fail(record, QIF_E_BADSTATE);
+
+    txn = qif_txn_new();
+    txn->txn_type = ctx->parse_type;
+
+    for (; record; record = record->next)
+    {
+        line = record->data;
+
+        switch (line->type)
+        {
+        case 'D':			/* D : transaction date */
+            qif_save_str(txn->datestr);
+            break;
+        case 'P':			/* P : payee */
+            qif_save_str(txn->payee);
+            break;
+        case 'A':			/* A : address */
+            /* multiple 'A' lines are appended together with newlines */
+            if (txn->address)
+            {
+                char *tmp = txn->address;
+                txn->address = g_strconcat(tmp, "\n", line->line, NULL);
+                g_free(tmp);
+            }
+            else
+                qif_save_str(txn->address);
+            break;
+        case 'N':			/* N : check/transaction number */
+            qif_save_str(txn->num);
+            break;
+        case 'C':			/* C : transaction cleared flag */
+            txn->cleared = qif_parse_cleared(line);
+            break;
+        case 'L':			/* L : default split category */
+            if (!txn->current_split) qif_save_str(txn->default_split->catstr);
+            break;
+        case 'M':			/* M : default split memo */
+            if (!txn->current_split) qif_save_str(txn->default_split->memo);
+            break;
+        case 'T':			/* T : total transaction amount */
+            if (!txn->current_split && !qif_is_bad_numeric_string(line->line))
+                qif_save_str(txn->default_split->amountstr);
+            break;
+        case 'S':			/* S : split category */
+            /* This implies a quicken-style "split transaction", so we're mostly
+             * going to ignore the default_split except for internal verification.
+             */
+            txn->current_split = qif_split_new();
+            txn->splits = g_list_prepend(txn->splits, txn->current_split);
+            qif_save_str(txn->current_split->catstr);
+            break;
+        case 'E':			/* E : split memo */
+            if (txn->current_split)
+                qif_save_str(txn->current_split->memo);
+            break;
+        case '$':			/* split amount */
+            if (txn->current_split && !qif_is_bad_numeric_string(line->line))
+                qif_save_str(txn->current_split->amountstr);
+            break;
+        default:
+            PERR("Unknown QIF transaction data at line %d: %s", line->lineno, line->line);
+        }
+    }
+
+    /* If we have no date string then there is no reason to do anything else */
+    if (txn->datestr)
+    {
+        /* We delay processing the date and amount strings until later.. */
+
+        /* parse the category on each split */
+        for (node = txn->splits; node; node = node->next)
+        {
+            split = node->data;
+            if (split->catstr)
+                qif_split_parse_category(ctx, split);
+        }
+        /* ... including the default split */
+        if (txn->default_split->catstr)
+            qif_split_parse_category(ctx, txn->default_split);
+
+        /* if we don't have an account, then deal with the opening balance */
+        if (!ctx->current_acct)
+            qif_process_opening_balance_txn(ctx, txn);
+
+        /* Set the transaction's from account */
+        txn->from_acct = ctx->current_acct;
+
+        /* And add it to the process list */
+        ctx->parse_state = g_list_prepend(ctx->parse_state, txn);
+
+    }
+    else
+        /* no date?  Ignore this txn */
+        qif_txn_destroy((QifObject)txn);
+
+    return QIF_E_OK;
+}
+
+/* after we parse the amounts, fix up the transaction splits */
+void
+qif_txn_setup_splits(QifTxn txn)
+{
+    QifSplit split, this_split;
+    GList *node;
+    gnc_numeric total;
+
+    if (txn->splits)
+    {
+        /* We have a bunch of "far" splits -- maybe fix up the totals.. */
+        qif_txn_fix_amounts(txn, txn->default_split->amount);
+
+        /* Re-Compute the total for the "near" (default) split */
+        total = gnc_numeric_zero();
+        for (node = txn->splits; node; node = node->next)
+        {
+            split = node->data;
+            split->value = split->amount;
+            total = gnc_numeric_add(total, split->amount, 0, GNC_HOW_DENOM_LCD);
+        }
+
+        /* And re-set the default-split amount */
+        txn->default_split->amount = gnc_numeric_neg(total);
+
+    }
+    else
+    {
+        /* not a split txn.  Compute the "far" split by copying the "near"
+         *  split and then moving the 'near' split to the far split.
+         */
+
+        /* First make a copy of this transaction and move the copy to the 'near' */
+        split = txn->default_split;
+        this_split = qif_split_copy(split);
+        txn->default_split = this_split;
+
+        /* then adjust the 'far' txn */
+        split->amount = gnc_numeric_neg(split->amount);
+        split->value = split->amount;
+        txn->splits = g_list_prepend(txn->splits, split);
+    }
+
+    /* Set the default-split value from the default-split amount */
+    txn->default_split->value = txn->default_split->amount;
+}
+
+/* This is called when we're done processing an account.  We want
+ * to merge the transactions in the "parse_state" into the Qif Context
+ */
+static QifError
+qif_txn_end_acct(QifContext ctx)
+{
+    GList *node;
+    QifTxn txn;
+    gboolean txn_needs_acct;
+
+    g_return_val_if_fail(ctx, QIF_E_INTERNAL);
+
+    /* Return now if there is nothing to do. */
+    if (!ctx->parse_state) return QIF_E_OK;
+
+    /* Walk through the list of transactions.  First check if it
+     * needs a from-account; then add it to the context.
+     */
+
+    txn_needs_acct = (ctx->parse_flags & QIF_F_TXN_NEEDS_ACCT);
+
+    /* Invert the list so we're working in the right order */
+    ctx->parse_state = g_list_reverse(ctx->parse_state);
+
+    for (node = ctx->parse_state; node; node = node->next)
+    {
+        txn = node->data;
+
+        /* If we need a from account, then set it.. */
+        if (txn_needs_acct && ctx->opening_bal_acct && !txn->from_acct)
+            txn->from_acct = ctx->opening_bal_acct;
+
+        /* merge the txn into the context (prepends to the list) */
+        qif_object_list_insert(ctx, (QifObject)txn);
+    }
+
+    if (txn_needs_acct && ctx->opening_bal_acct)
+        qif_clear_flag(ctx->parse_flags, QIF_F_TXN_NEEDS_ACCT);
+
+    /* clean up our state */
+    g_list_free(ctx->parse_state);
+    ctx->parse_state = NULL;
+
+    return QIF_E_OK;
+}
+
+/* Extra info in an Investment Transaction */
+static QifInvstTxn
+qif_invst_txn_new(void)
+{
+    QifInvstTxn itxn = g_new0(struct _QifInvstTxn, 1);
+
+    itxn->amount = gnc_numeric_zero();
+    itxn->d_amount = gnc_numeric_zero();
+    itxn->price = gnc_numeric_zero();
+    itxn->shares = gnc_numeric_zero();
+    itxn->commission = gnc_numeric_zero();
+
+    return itxn;
+}
+
+static void
+qif_txn_invst_destroy(QifInvstTxn itxn)
+{
+    if (!itxn) return;
+
+    g_free(itxn->amountstr);
+    g_free(itxn->d_amountstr);
+    g_free(itxn->pricestr);
+    g_free(itxn->sharesstr);
+    g_free(itxn->commissionstr);
+    g_free(itxn->security);
+
+    g_free(itxn->catstr);
+
+    g_free(itxn);
+}
+
+static QifError
+qif_txn_invst_parse(QifContext ctx, GList *record)
+{
+    QifTxn txn;
+    QifInvstTxn itxn;
+    QifLine line;
+
+    g_return_val_if_fail(ctx, QIF_E_INTERNAL);
+    g_return_val_if_fail(record, QIF_E_BADSTATE);
+
+    txn = qif_txn_new();
+    txn->txn_type = ctx->parse_type;
+    itxn = qif_invst_txn_new();
+    txn->invst_info = itxn;
+
+    for (; record; record = record->next)
+    {
+        line = record->data;
+
+        switch (line->type)
+        {
+        case 'D':			/* D : transaction date */
+            qif_save_str(txn->datestr);
+            break;
+        case 'P':			/* P : txn payee */
+            qif_save_str(txn->payee);
+            break;
+        case 'N':			/* N : action */
+            itxn->action = qif_parse_action(line);
+            break;
+        case 'C':			/* C : cleared flag */
+            txn->cleared = qif_parse_cleared(line);
+            break;
+        case 'M':			/* M : memo */
+            if (!txn->current_split)
+                qif_save_str(txn->default_split->memo);
+            break;
+        case 'T':			/* T : total amount */
+            if (!qif_is_bad_numeric_string(line->line))
+                qif_save_str(itxn->amountstr);
+            break;
+        case '$':			/* $ : transfer amount */
+            if (!qif_is_bad_numeric_string(line->line))
+                qif_save_str(itxn->d_amountstr);
+            break;
+        case 'I':			/* I : share price */
+            qif_save_str(itxn->pricestr);
+            break;
+        case 'Q':			/* Q : number of shares */
+            qif_save_str(itxn->sharesstr);
+            break;
+        case 'Y':			/* Y : name of security */
+            qif_save_str(itxn->security);
+            break;
+        case 'O':			/* O : commission */
+            qif_save_str(itxn->commissionstr);
+            break;
+        case 'L':			/* L : category */
+            qif_save_str(itxn->catstr);
+            break;
+        default:
+            PERR("Unknown QIF Investment transaction data at line %d: %s",
+                 line->lineno, line->line);
+        }
+    }
+
+    /* If we have no date string then there is no reason to do anything else */
+    if (txn->datestr && itxn->action != QIF_A_NONE)
+    {
+
+        /* Make sure we've got a security name */
+        if (!itxn->security)
+            itxn->security = g_strdup("");	/* XXX */
+
+        /* if we don't have a from account, then mark the fact that
+         * we'll need one later.
+         */
+        if (ctx->current_acct)
+            txn->from_acct = ctx->current_acct;
+        else
+            qif_set_flag(ctx->parse_flags, QIF_F_ITXN_NEEDS_ACCT);
+
+        /* Add this transaction to the parse state for later processing */
+        ctx->parse_state = g_list_prepend(ctx->parse_state, txn);
+
+    }
+    else
+    {
+        /* no date?  Just destroy it */
+        qif_txn_destroy((QifObject)txn);
+    }
+
+    return QIF_E_OK;
+}
+
+
+void
+qif_invst_txn_setup_splits(QifContext ctx, QifTxn txn)
+{
+    QifInvstTxn itxn;
+    QifSplit near_split, far_split, comm_split;
+    QifAccount from_acct;
+
+    char *cat = NULL;
+    char *cat_class = NULL;
+    gboolean cat_is_acct = FALSE;
+    char *miscx = NULL;
+    char *miscx_class = NULL;
+    gboolean miscx_is_acct = FALSE;
+
+    /* Cached account-type lists */
+    static GList *bank_list = NULL;
+
+    gnc_numeric split_value;
+
+    g_return_if_fail(ctx);
+    g_return_if_fail(txn);
+    g_return_if_fail(txn->invst_info);
+
+    itxn = txn->invst_info;
+
+    /* Compute the share value, because we'll probably need it */
+    split_value = gnc_numeric_mul(itxn->shares, itxn->price, 0, GNC_HOW_DENOM_REDUCE);
+
+    /* Make sure that "amount" is a valid "transaction amount" */
+    if (!itxn->amountstr && itxn->d_amountstr)
+        itxn->amount = itxn->d_amount;
+
+    /* near and far splits..  for simplicity */
+    near_split = txn->default_split;
+    far_split = qif_split_new();
+    from_acct = txn->from_acct;
+
+    /* Parse the category string */
+    if (!qif_parse_split_category(itxn->catstr,
+                                  &cat, &cat_is_acct, &cat_class,
+                                  &miscx, &miscx_is_acct, &miscx_class))
+        PERR("Failure parsing category: %s", itxn->catstr);
+
+    /* Make sure we've got a cached list */
+    if (bank_list == NULL)
+        bank_list = qif_parse_acct_type("__any_bank__", -1);
+
+    /* find the NEAR account */
+
+    switch (itxn->action)
+    {
+    case QIF_A_BUY:
+    case QIF_A_BUYX:
+    case QIF_A_REINVDIV:
+    case QIF_A_REINVINT:
+    case QIF_A_REINVLG:
+    case QIF_A_REINVMD:
+    case QIF_A_REINVSG:
+    case QIF_A_REINVSH:
+    case QIF_A_SELL:
+    case QIF_A_SELLX:
+    case QIF_A_SHRSIN:
+    case QIF_A_SHRSOUT:
+    case QIF_A_STKSPLIT:
+        txn->from_acct = qif_default_stock_acct(ctx, itxn->security);
+        break;
+
+    case QIF_A_CGLONG:
+    case QIF_A_CGMID:
+    case QIF_A_CGSHORT:
+    case QIF_A_DIV:
+    case QIF_A_INTINC:
+    case QIF_A_MARGINT:
+    case QIF_A_MISCEXP:
+    case QIF_A_MISCINC:
+    case QIF_A_RTRNCAP:
+    case QIF_A_XIN:
+    case QIF_A_XOUT:
+        txn->from_acct = from_acct;
+        break;
+
+    case QIF_A_CGLONGX:
+    case QIF_A_CGMIDX:
+    case QIF_A_CGSHORTX:
+    case QIF_A_DIVX:
+    case QIF_A_INTINCX:
+    case QIF_A_MARGINTX:
+    case QIF_A_RTRNCAPX:
+        txn->from_acct = find_or_make_acct(ctx, cat, bank_list);
+        cat = NULL;
+        break;
+
+    case QIF_A_MISCEXPX:
+    case QIF_A_MISCINCX:
+        txn->from_acct = find_or_make_acct(ctx, miscx, bank_list);
+        miscx = NULL;
+        break;
+
+    default:
+        PERR("Unhandled Action: %d", itxn->action);
+        break;
+    }
+
+    /* find the FAR account */
+
+    itxn->far_cat_is_acct = TRUE;
+    switch (itxn->action)
+    {
+    case QIF_A_BUY:
+    case QIF_A_SELL:
+        itxn->far_cat.acct = from_acct;
+        break;
+
+    case QIF_A_BUYX:
+    case QIF_A_MISCEXP:
+    case QIF_A_MISCEXPX:
+    case QIF_A_MISCINC:
+    case QIF_A_MISCINCX:
+    case QIF_A_SELLX:
+    case QIF_A_XIN:
+    case QIF_A_XOUT:
+        itxn->far_cat.cat = find_or_make_cat(ctx, cat);
+        itxn->far_cat_is_acct = FALSE;
+        cat = NULL;
+        break;
+
+    case QIF_A_CGLONG:
+    case QIF_A_CGLONGX:
+    case QIF_A_REINVLG:
+        itxn->far_cat.acct = qif_default_cglong_acct(ctx, itxn->security);
+        break;
+
+    case QIF_A_CGMID:
+    case QIF_A_CGMIDX:
+    case QIF_A_REINVMD:
+        itxn->far_cat.acct = qif_default_cgmid_acct(ctx, itxn->security);
+        break;
+
+    case QIF_A_CGSHORT:
+    case QIF_A_CGSHORTX:
+    case QIF_A_REINVSG:
+    case QIF_A_REINVSH:
+        itxn->far_cat.acct = qif_default_cgshort_acct(ctx, itxn->security);
+        break;
+
+    case QIF_A_DIV:
+    case QIF_A_DIVX:
+    case QIF_A_REINVDIV:
+        itxn->far_cat.acct = qif_default_dividend_acct(ctx, itxn->security);
+        break;
+
+    case QIF_A_INTINC:
+    case QIF_A_INTINCX:
+    case QIF_A_REINVINT:
+        itxn->far_cat.acct = qif_default_interest_acct(ctx, itxn->security);
+        break;
+
+    case QIF_A_MARGINT:
+    case QIF_A_MARGINTX:
+        itxn->far_cat.acct = qif_default_margin_interest_acct(ctx);
+        break;
+
+    case QIF_A_RTRNCAP:
+    case QIF_A_RTRNCAPX:
+        itxn->far_cat.acct = qif_default_capital_return_acct(ctx, itxn->security);
+        break;
+
+    case QIF_A_SHRSIN:
+    case QIF_A_SHRSOUT:
+        itxn->far_cat.acct = qif_default_equity_holding(ctx, itxn->security);
+        break;
+
+    case QIF_A_STKSPLIT:
+        itxn->far_cat.acct = qif_default_stock_acct(ctx, itxn->security);
+        break;
+
+    default:
+        break;
+    }
+
+    /* If we don't have a far acct (or far category) then reset the flag */
+    if (!itxn->far_cat.obj)
+        itxn->far_cat_is_acct = FALSE;
+
+    /* And now fill in the "near" and "far" splits.  In particular we need
+     *
+     *	NEAR:	txn->from_acct, near_split->amount, value
+     *	FAR:	cat, far_split->amount, value
+     */
+    switch (itxn->action)
+    {
+    case QIF_A_BUY:
+    case QIF_A_BUYX:
+    case QIF_A_REINVDIV:
+    case QIF_A_REINVINT:
+    case QIF_A_REINVLG:
+    case QIF_A_REINVMD:
+    case QIF_A_REINVSG:
+    case QIF_A_REINVSH:
+    case QIF_A_SHRSIN:
+        near_split->amount = itxn->shares;
+        near_split->value = split_value;
+        far_split->amount = far_split->value = gnc_numeric_neg(itxn->amount);
+        break;
+
+    case QIF_A_SELL:
+    case QIF_A_SELLX:
+    case QIF_A_SHRSOUT:
+        near_split->amount = gnc_numeric_neg(itxn->shares);
+        near_split->value = gnc_numeric_neg(split_value);
+        far_split->amount = far_split->value = itxn->amount;
+        break;
+
+    case QIF_A_CGLONG:
+    case QIF_A_CGLONGX:
+    case QIF_A_CGMID:
+    case QIF_A_CGMIDX:
+    case QIF_A_CGSHORT:
+    case QIF_A_CGSHORTX:
+    case QIF_A_DIV:
+    case QIF_A_DIVX:
+    case QIF_A_INTINC:
+    case QIF_A_INTINCX:
+    case QIF_A_MISCINC:
+    case QIF_A_MISCINCX:
+    case QIF_A_RTRNCAP:
+    case QIF_A_RTRNCAPX:
+    case QIF_A_XIN:
+        near_split->amount = near_split->value = itxn->amount;
+        far_split->amount = far_split->value = gnc_numeric_neg(itxn->amount);
+        break;
+
+    case QIF_A_MARGINT:
+    case QIF_A_MARGINTX:
+    case QIF_A_MISCEXP:
+    case QIF_A_MISCEXPX:
+    case QIF_A_XOUT:
+        near_split->amount = near_split->value = gnc_numeric_neg(itxn->amount);
+        far_split->amount = far_split->value = itxn->amount;
+        break;
+
+    case QIF_A_STKSPLIT:
+        /* QIF just specifies the split ratio, not the number of shares
+         * in and out, so we have to fetch the number of shares from the
+         * security account..  FEH!
+         */
+
+        near_split->value = gnc_numeric_neg(split_value);
+        far_split->value = split_value;
+
+        /* XXX: FIXME: compute in-shares/out-shares based on ratio here:
+         *
+         * splitratio = num-shares / 10;
+         * in_shares = gnc_account_get_balance(near_acct);
+         * out_shares = in_shares * splitratio;
+         *
+         * near_split->amount = out_shares;
+         * far_split->amount = gnc_numeric_neg(in_shares);
+         *
+         * We know (later) that near_split == txn->default_split and
+         * far_split == txn->splits->data, so we'll just special-case this
+         * kind of txn when we convert to GNC later.
+         */
+
+        break;
+
+    default:
+        break;
+    }
+
+    /* Just make sure to set that it's an account, not a category */
+    far_split->cat.obj = itxn->far_cat.obj;
+    if (itxn->far_cat_is_acct)
+        far_split->cat_is_acct = TRUE;
+
+    /* make the commission split if we need it, then add it to the split-list  */
+    if (itxn->commissionstr)
+    {
+        comm_split = qif_split_new();
+        comm_split->cat.acct = qif_default_commission_acct(ctx);
+        comm_split->cat_is_acct = TRUE;
+        comm_split->amount = itxn->commission;
+        comm_split->value = itxn->commission;
+
+        txn->splits = g_list_prepend(txn->splits, comm_split);
+    }
+
+    /* Push the "far split" into the txn split-list */
+    txn->splits = g_list_prepend(txn->splits, far_split);
+
+    /* Free parsed strings.. */
+    g_free(cat);
+    g_free(cat_class);
+    g_free(miscx);
+    g_free(miscx_class);
+}
+
+
+/* Other handlers */
+static void
+qif_autoswitch_set(QifContext ctx)
+{
+    qif_set_flag(ctx->parse_flags, QIF_F_IGNORE_ACCOUNTS);
+}
+
+static void
+qif_autoswitch_clear(QifContext ctx)
+{
+    qif_clear_flag(ctx->parse_flags, QIF_F_IGNORE_ACCOUNTS);
+}
+
+/********************************************************************************
+ * find or make ...
+ */
+
+QifAccount
+find_or_make_acct(QifContext ctx, char *name, GList *types)
+{
+    QifAccount res;
+
+    res = (QifAccount)qif_object_map_lookup(ctx, QIF_O_ACCOUNT, name);
+    if (res)
+        g_free(name);
+    else
+    {
+        res = qif_account_new();
+        res->name = name;
+        res->type_list = types;
+
+        qif_object_map_insert(ctx, name, (QifObject)res);
+    }
+
+    return res;
+}
+
+QifCategory
+find_or_make_cat(QifContext ctx, char *name)
+{
+    QifCategory res;
+
+    res = (QifCategory)qif_object_map_lookup(ctx, QIF_O_CATEGORY, name);
+    if (res)
+        g_free(name);
+    else
+    {
+        res = qif_cat_new();
+
+        res->name = name;
+
+        qif_object_map_insert(ctx, name, (QifObject)res);
+    }
+
+    return res;
+}
+
+QifClass
+find_or_make_class(QifContext ctx, char *name)
+{
+    QifClass res;
+
+    res = (QifClass)qif_object_map_lookup(ctx, QIF_O_CLASS, name);
+    if (res)
+        g_free(name);
+    else
+    {
+        res = qif_class_new();
+        res->name = name;
+        qif_object_map_insert(ctx, name, (QifObject)res);
+    }
+    return res;
+}
+
+/*****************************************************************************/
+
+/*
+ * initialize handlers
+ */
+void
+qif_object_init(void)
+{
+    int i;
+    static struct
+    {
+        QifType		type;
+        struct _QifHandler	handler;
+    } handlers[] =
+    {
+        { QIF_TYPE_BANK, { qif_txn_init, qif_txn_parse, qif_txn_end_acct } },
+        { QIF_TYPE_CASH, { qif_txn_init, qif_txn_parse, qif_txn_end_acct } },
+        { QIF_TYPE_CCARD, { qif_txn_init, qif_txn_parse, qif_txn_end_acct } },
+        { QIF_TYPE_INVST, { qif_txn_init, qif_txn_invst_parse, qif_txn_end_acct } },
+        { QIF_TYPE_PORT, { qif_txn_init, qif_txn_invst_parse, qif_txn_end_acct } },
+        { QIF_TYPE_OTH_A, { qif_txn_init, qif_txn_parse, qif_txn_end_acct } },
+        { QIF_TYPE_OTH_L, { qif_txn_init, qif_txn_parse, qif_txn_end_acct } },
+        { QIF_TYPE_CLASS, { NULL, qif_class_parse, NULL } },
+        { QIF_TYPE_CAT, { NULL, qif_cat_parse, NULL } },
+        { QIF_TYPE_SECURITY, { NULL, qif_security_parse, NULL } },
+        { QIF_ACCOUNT, { NULL, qif_account_parse, NULL } },
+        { QIF_AUTOSWITCH, { qif_autoswitch_set, NULL, NULL } },
+        { QIF_CLEAR_AUTOSWITCH, { qif_autoswitch_clear, NULL, NULL } },
+        { 0, {NULL, NULL, NULL} }
+    };
+
+    for (i = 0; handlers[i].type > 0; i++)
+    {
+        if (handlers[i].type <= 0)
+        {
+            PERR("Invalid type?!?  (%d @ %d)", handlers[i].type, i);
+        }
+        else
+            qif_register_handler(handlers[i].type, &(handlers[i].handler));
+    }
+}
diff --git a/gnucash/import-export/qif/qif-objects.h b/gnucash/import-export/qif/qif-objects.h
new file mode 100644
index 0000000..2d28e59
--- /dev/null
+++ b/gnucash/import-export/qif/qif-objects.h
@@ -0,0 +1,71 @@
+/*
+ * qif-objects.h -- QIF objects for the QIF importer
+ *
+ * Written By:	Derek Atkins <derek at ihtfp.com>
+ * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ */
+
+#ifndef QIF_OBJECTS_H
+#define QIF_OBJECTS_H
+
+typedef struct _QifObject *QifObject;
+typedef struct _QifData *QifData;
+
+struct _QifObject
+{
+    const char*	type;
+    void		(*destroy)(QifObject);
+
+    /* QIF Objects contain data beyond this point.. */
+};
+
+#define QIF_O_ACCOUNT	"qif-acct"
+typedef struct _QifAccount *QifAccount;
+
+#define QIF_O_CATEGORY	"qif-cat"
+typedef struct _QifCategory *QifCategory;
+
+#define QIF_O_CLASS	"qif-class"
+typedef struct _QifClass *QifClass;
+
+#define QIF_O_SECURITY	"qif-security"
+typedef struct _QifSecurity *QifSecurity;
+
+#define QIF_O_TXN	"qif-txn"
+typedef struct _QifTxn *QifTxn;
+typedef struct _QifSplit *QifSplit;
+typedef struct _QifInvstTxn *QifInvstTxn;
+
+void qif_object_init(void);
+
+QifAccount find_or_make_acct(QifContext ctx, char *name, GList *types);
+QifCategory find_or_make_cat(QifContext ctx, char *name);
+QifClass find_or_make_class(QifContext ctx, char *name);
+
+/* merge the object into the context.  Returns the object that's in
+ * the context, which is either the supplied object or the
+ * already-existing object.
+ */
+QifAccount qif_account_merge(QifContext ctx, QifAccount acct);
+QifCategory qif_cat_merge(QifContext ctx, QifCategory cat);
+QifClass qif_class_merge(QifContext ctx, QifClass qclass);
+QifSecurity qif_security_merge(QifContext ctx, QifSecurity security);
+
+#endif /* QIF_OBJECTS_H */
diff --git a/gnucash/import-export/qif/qif-parse.c b/gnucash/import-export/qif/qif-parse.c
new file mode 100644
index 0000000..f291c86
--- /dev/null
+++ b/gnucash/import-export/qif/qif-parse.c
@@ -0,0 +1,935 @@
+/*
+ * qif-parse.c -- parse QIF
+ *
+ * Written by:        Derek Atkins <derek at ihtfp.com>
+ * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <string.h>
+
+/* For regex */
+#include <sys/types.h>
+#include <regex.h>
+
+#include <stdarg.h>
+
+#include "gnc-engine.h"
+#include "gnc-ui-util.h"
+
+#include "qif-import-p.h"
+#include "qif-objects-p.h"
+
+#include "import-parse.h"
+
+static QofLogModule log_module = GNC_MOD_IMPORT;
+
+/* An array of handlers for the various bang-types */
+static QifHandler qif_handlers[QIF_TYPE_MAX+1] = { NULL };
+
+/* Parser Regular Expressions */
+static gboolean qifp_regex_compiled = FALSE;
+static regex_t category_regex;
+
+/* A Hash Table of bang-types */
+static GHashTable *qif_bangtype_map = NULL;
+
+/* A Hash Table of action strings */
+static GHashTable *qif_action_map = NULL;
+
+/* A Hash Table of account types */
+static GHashTable *qif_atype_map = NULL;
+
+/************************************************************************/
+
+/* Register a handler */
+void
+qif_register_handler(QifType type, QifHandler handler)
+{
+    if (type <= 0 || type > QIF_TYPE_MAX)
+    {
+        PERR("Invalid type: %d", type);
+        return;
+    }
+    qif_handlers[type] = handler;
+}
+
+static void
+compile_regex()
+{
+    regcomp(&category_regex,
+            "^ *(\\[)?([^]/|]*)(]?)(/([^|]*))?(\\|(\\[)?([^]/]*)(]?)(/(.*))?)? *$",
+            REG_EXTENDED);
+
+    qifp_regex_compiled = TRUE;
+}
+
+#define QIF_ADD_TYPE(ts,t) \
+        g_hash_table_insert(qif_bangtype_map, ts, GINT_TO_POINTER(t)); \
+        g_hash_table_insert(qif_bangtype_map, _(ts), GINT_TO_POINTER(t));
+
+static void
+build_bangtype_map()
+{
+    g_return_if_fail(!qif_bangtype_map);
+
+    qif_bangtype_map = g_hash_table_new(g_str_hash, g_str_equal);
+    g_assert(qif_bangtype_map);
+
+    /* Translators FIXME: It is unclear whether these strings should
+       really be translated, and if yes, into which translation. */
+    QIF_ADD_TYPE(N_("type:bank"), QIF_TYPE_BANK);
+    QIF_ADD_TYPE(N_("type:cash"), QIF_TYPE_CASH);
+    QIF_ADD_TYPE(N_("type:ccard"), QIF_TYPE_CCARD);
+    QIF_ADD_TYPE(N_("type:invst"), QIF_TYPE_INVST);
+    QIF_ADD_TYPE(N_("type:port"), QIF_TYPE_PORT);
+    QIF_ADD_TYPE(N_("type:oth a"), QIF_TYPE_OTH_A);
+    QIF_ADD_TYPE(N_("type:oth l"), QIF_TYPE_OTH_L);
+    QIF_ADD_TYPE(N_("type:class"), QIF_TYPE_CLASS);
+    QIF_ADD_TYPE(N_("type:cat"), QIF_TYPE_CAT);
+    QIF_ADD_TYPE(N_("type:security"), QIF_TYPE_SECURITY);
+    QIF_ADD_TYPE(N_("account"), QIF_ACCOUNT);
+    QIF_ADD_TYPE(N_("option:autoswitch"), QIF_AUTOSWITCH);
+    QIF_ADD_TYPE(N_("clear:autoswitch"), QIF_CLEAR_AUTOSWITCH);
+}
+#undef QIF_ADD_TYPE
+
+#define QIF_ADD_ACT(ts,t) \
+        g_hash_table_insert(qif_action_map, ts, GINT_TO_POINTER(t));
+
+static void
+build_action_map()
+{
+    g_return_if_fail(!qif_action_map);
+
+    qif_action_map = g_hash_table_new(g_str_hash, g_str_equal);
+    g_assert(qif_action_map);
+
+    QIF_ADD_ACT("buy", QIF_A_BUY);
+    QIF_ADD_ACT("cvrshrt", QIF_A_BUY);
+    QIF_ADD_ACT("kauf", QIF_A_BUY);
+    QIF_ADD_ACT("buyx", QIF_A_BUYX);
+    QIF_ADD_ACT("cvrshrtx", QIF_A_BUYX);
+    QIF_ADD_ACT("kaufx", QIF_A_BUYX);
+    QIF_ADD_ACT("cglong", QIF_A_CGLONG);
+    QIF_ADD_ACT("kapgew", QIF_A_CGLONG); /* Kapitalgewinnsteuer */
+    QIF_ADD_ACT("cglongx", QIF_A_CGLONG);
+    QIF_ADD_ACT("kapgewx", QIF_A_CGLONG);
+    QIF_ADD_ACT("cgmid", QIF_A_CGMID);
+    QIF_ADD_ACT("cgmidx", QIF_A_CGMIDX);
+    QIF_ADD_ACT("cgshort", QIF_A_CGSHORT);
+    QIF_ADD_ACT("k.gewsp", QIF_A_CGSHORT);
+    QIF_ADD_ACT("cgshortx", QIF_A_CGSHORTX);
+    QIF_ADD_ACT("k.gewspx", QIF_A_CGSHORTX);
+    QIF_ADD_ACT("div", QIF_A_DIV); /* dividende */
+    QIF_ADD_ACT("divx", QIF_A_DIVX);
+    //QIF_ADD_ACT("exercise", QIF_A_EXERCISE);
+    //QIF_ADD_ACT("exercisex", QIF_A_EXERCISEX);
+    //QIF_ADD_ACT("expire", QIF_A_EXPIRE);
+    //QIF_ADD_ACT("grant", QIF_A_GRANT);
+    QIF_ADD_ACT("int", QIF_A_INTINC);
+    QIF_ADD_ACT("intinc", QIF_A_INTINC);
+    QIF_ADD_ACT("aktzu", QIF_A_INTINC); /* zinsen */
+    QIF_ADD_ACT("intx", QIF_A_INTINCX);
+    QIF_ADD_ACT("intincx", QIF_A_INTINCX);
+    QIF_ADD_ACT("margint", QIF_A_MARGINT);
+    QIF_ADD_ACT("margintx", QIF_A_MARGINTX);
+    QIF_ADD_ACT("miscexp", QIF_A_MISCEXP);
+    QIF_ADD_ACT("miscexpx", QIF_A_MISCEXPX);
+    QIF_ADD_ACT("miscinc", QIF_A_MISCINC);
+    QIF_ADD_ACT("cash", QIF_A_MISCINC);
+    QIF_ADD_ACT("miscincx", QIF_A_MISCINCX);
+    QIF_ADD_ACT("reinvdiv", QIF_A_REINVDIV);
+    QIF_ADD_ACT("reinvint", QIF_A_REINVINT);
+    QIF_ADD_ACT("reinvzin", QIF_A_REINVINT);
+    QIF_ADD_ACT("reinvlg", QIF_A_REINVLG);
+    QIF_ADD_ACT("reinvkur", QIF_A_REINVLG);
+    QIF_ADD_ACT("reinvmd", QIF_A_REINVMD);
+    QIF_ADD_ACT("reinvsg", QIF_A_REINVSG);
+    QIF_ADD_ACT("reinvksp", QIF_A_REINVSG);
+    QIF_ADD_ACT("reinvsh", QIF_A_REINVSH);
+    QIF_ADD_ACT("reminder", QIF_A_REMINDER);
+    QIF_ADD_ACT("erinnerg", QIF_A_REMINDER);
+    QIF_ADD_ACT("rtrncap", QIF_A_RTRNCAP);
+    QIF_ADD_ACT("rtrncapx", QIF_A_RTRNCAPX);
+    QIF_ADD_ACT("sell", QIF_A_SELL);
+    QIF_ADD_ACT("shtsell", QIF_A_SELL);
+    QIF_ADD_ACT("verkauf", QIF_A_SELL); /* verkaufen */
+    QIF_ADD_ACT("sellx", QIF_A_SELLX);
+    QIF_ADD_ACT("shtsellx", QIF_A_SELLX);
+    QIF_ADD_ACT("verkaufx", QIF_A_SELLX); /* verkaufen */
+    QIF_ADD_ACT("shrsin", QIF_A_SHRSIN);
+    QIF_ADD_ACT("aktzu", QIF_A_SHRSIN);
+    QIF_ADD_ACT("shrsout", QIF_A_SHRSOUT);
+    QIF_ADD_ACT("aktab", QIF_A_SHRSOUT);
+    QIF_ADD_ACT("stksplit", QIF_A_STKSPLIT);
+    QIF_ADD_ACT("aktsplit", QIF_A_STKSPLIT);
+    //QIF_ADD_ACT("vest", QIF_A_VEST);
+    QIF_ADD_ACT("xin", QIF_A_XIN);
+    QIF_ADD_ACT("contribx", QIF_A_XIN);
+    QIF_ADD_ACT("xout", QIF_A_XOUT);
+    QIF_ADD_ACT("withdrwx", QIF_A_XOUT);
+}
+#undef QIF_ADD_ACT
+
+static GList *
+make_list(int count, ...)
+{
+    GList *result = NULL;
+    GNCAccountType type;
+    va_list ap;
+
+    va_start (ap, count);
+    while (count--)
+    {
+        type = va_arg (ap, GNCAccountType);
+        result = g_list_prepend (result, GINT_TO_POINTER(type));
+    }
+    va_end (ap);
+
+
+    return g_list_reverse(result);
+}
+
+#define QIF_ADD_ATYPE(a,t) g_hash_table_insert(qif_atype_map, a, t);
+static void
+build_atype_map()
+{
+    g_return_if_fail(!qif_atype_map);
+
+    qif_atype_map = g_hash_table_new(g_str_hash, g_str_equal);
+    g_assert(qif_atype_map);
+
+    QIF_ADD_ATYPE("bank", make_list(1, ACCT_TYPE_BANK));
+    QIF_ADD_ATYPE("port", make_list(1, ACCT_TYPE_BANK));
+    QIF_ADD_ATYPE("cash", make_list(1, ACCT_TYPE_CASH));
+    QIF_ADD_ATYPE("ccard", make_list(1, ACCT_TYPE_CREDIT));
+    QIF_ADD_ATYPE("invst", make_list(3, ACCT_TYPE_BANK, ACCT_TYPE_STOCK,
+                                     ACCT_TYPE_MUTUAL));
+    QIF_ADD_ATYPE("oth a", make_list(3, ACCT_TYPE_ASSET, ACCT_TYPE_BANK,
+                                     ACCT_TYPE_CASH));
+    QIF_ADD_ATYPE("oth l", make_list(2, ACCT_TYPE_LIABILITY, ACCT_TYPE_CREDIT));
+    QIF_ADD_ATYPE("mutual", make_list(3, ACCT_TYPE_BANK, ACCT_TYPE_MUTUAL,
+                                      ACCT_TYPE_STOCK));
+
+    /* Internal types */
+    QIF_ADD_ATYPE("__any_bank__", make_list(5, ACCT_TYPE_BANK, ACCT_TYPE_CREDIT,
+                                            ACCT_TYPE_CASH, ACCT_TYPE_ASSET,
+                                            ACCT_TYPE_LIABILITY));
+    QIF_ADD_ATYPE("__all__", make_list(7, ACCT_TYPE_BANK, ACCT_TYPE_CREDIT,
+                                       ACCT_TYPE_CASH, ACCT_TYPE_ASSET,
+                                       ACCT_TYPE_LIABILITY, ACCT_TYPE_STOCK,
+                                       ACCT_TYPE_MUTUAL));
+    QIF_ADD_ATYPE("__stock__", make_list(2, ACCT_TYPE_STOCK, ACCT_TYPE_MUTUAL));
+    QIF_ADD_ATYPE("__income__", make_list(1, ACCT_TYPE_INCOME));
+    QIF_ADD_ATYPE("__expense__", make_list(1, ACCT_TYPE_EXPENSE));
+    QIF_ADD_ATYPE("__equity__", make_list(1, ACCT_TYPE_EQUITY));
+}
+#undef QIF_ADD_ATYPE
+
+/************************************************************************/
+
+/*
+ * We've got a !Type line.  Parse the line into the appropriate
+ * type and then initialize the handler.
+ */
+void
+qif_parse_bangtype(QifContext ctx, const char *line)
+{
+    QifType type;
+    char *bangtype;
+    gpointer result;
+
+    g_return_if_fail(line && *line == '!');
+
+    if (!qif_bangtype_map)
+        build_bangtype_map();
+
+    /* Make a local copy so we can manipulate it.
+     * - strip off leading/trailing whitespace
+     * - make it all lower case
+     */
+    bangtype = g_utf8_strdown(line + 1, -1);
+    g_strstrip(bangtype);
+
+    /* In some cases we get "!Type Bank" -- change the space to a colon */
+    if (!strncmp(bangtype, "type ", 5))
+        bangtype[5] = ':';
+
+    /* Lookup the bangtype in the map and then destroy the local copy */
+    result = g_hash_table_lookup(qif_bangtype_map, bangtype);
+    g_free(bangtype);
+
+    if (!result)
+    {
+        PWARN("Unknown bang-type at line %d: %s.  Ignored", ctx->lineno, line);
+        return;
+    }
+    type = GPOINTER_TO_INT(result);
+
+    /* Set the current context parse type and handler */
+    ctx->parse_type = type;
+    ctx->handler = qif_handlers[type];
+
+    /* now initialize this new parse type (if there's an init function) */
+    if (ctx->handler && ctx->handler->init)
+        ctx->handler->init(ctx);
+}
+
+/* returns TRUE if successful, FALSE if there is a problem */
+gboolean
+qif_parse_split_category(const char* str,
+                         char** cat, gboolean *cat_is_acct, char** cat_class,
+                         char** miscx_cat, gboolean *miscx_cat_is_acct,
+                         char **miscx_class)
+{
+    /* This is a pretty f**ked up string.  Basically it looks like:
+     *  ([)cat-or-acct(])(/(class))(|([)cat-of-acct(])(/ext))
+     *
+     * where data in parens is "optional" (depending on the context).
+     *
+     * examples from reality:
+     *
+     * category
+     * category:subcategory
+     * category/class
+     * category:subcat/class
+     * [account]
+     * [account]/class
+     *
+     * cat/cat-class|miscx-cat/miscx-class
+     */
+
+    regmatch_t pmatch[12];
+
+    g_return_val_if_fail(cat && cat_is_acct && cat_class &&
+                         miscx_cat && miscx_cat_is_acct && miscx_class, FALSE);
+
+
+    if (!qifp_regex_compiled)
+        compile_regex();
+
+    if (regexec(&category_regex, str, 12, pmatch, 0) != 0)
+    {
+        PERR("category match failed");
+        return FALSE;
+    }
+
+    /*
+     * what the substrings mean:
+     * 1 the opening [ for a transfer
+     * 2 the category
+     * 3 the closing ]
+     * 4 the class /
+     * 5 the class
+     * 6 the miscx expression (whole thing)
+     * 7 the opening [
+     * 8 the miscx category
+     * 9 the closing ]
+     * 10 the class /
+     * 11 the class
+     */
+
+    if (pmatch[2].rm_so == -1)
+    {
+        PERR("no category match found!");
+        return FALSE;
+    }
+
+    /* catgory name */
+    *cat = g_strndup(str + pmatch[2].rm_so, pmatch[2].rm_eo - pmatch[2].rm_so);
+    /* category is account? */
+    *cat_is_acct = (pmatch[1].rm_so != -1 && pmatch[3].rm_so != -1);
+    /* category class */
+    *cat_class = (pmatch[4].rm_so != -1 ?
+                  g_strndup(str + pmatch[5].rm_so, pmatch[5].rm_eo - pmatch[5].rm_so) :
+                  NULL);
+
+    /* miscx category name */
+    *miscx_cat = (pmatch[6].rm_so != -1 ?
+                  g_strndup(str + pmatch[8].rm_so, pmatch[8].rm_eo - pmatch[8].rm_so) :
+                  NULL);
+    /* miscx cat is acct */
+    *miscx_cat_is_acct  = (pmatch[7].rm_so != -1 && pmatch[9].rm_so != -1);
+    /* miscx class */
+    *miscx_class = (pmatch[10].rm_so != -1 ?
+                    g_strndup(str + pmatch[11].rm_so,
+                              pmatch[11].rm_eo - pmatch[11].rm_so) : NULL);
+
+    return TRUE;
+}
+
+/*
+ * qif_parse_cleared -- parse the 'C'leared field of a QIF Transaction.
+ * returns the QIF reconciled flag.
+ *
+ * * means cleared, x or X means reconciled, and ! or ? mean some
+ * budget related stuff I don't understand.
+ */
+QifRecnFlag
+qif_parse_cleared(QifLine line)
+{
+    g_return_val_if_fail(line, QIF_R_NO);
+    g_return_val_if_fail(line->line, QIF_R_NO);
+
+    switch (*line->line)
+    {
+    case '*':
+        return QIF_R_CLEARED;
+    case 'x':
+    case 'X':
+        return QIF_R_RECONCILED;
+    case '?':
+    case '!':
+        return QIF_R_BUDGETED;
+    default:
+        PERR("Unknown QIF Cleared flag at line %d: %s", line->lineno, line->line);
+        return QIF_R_NO;
+    }
+}
+
+QifAction qif_parse_action(QifLine line)
+{
+    QifAction qaction;
+    gpointer result;
+    char *action;
+
+    g_return_val_if_fail(line, QIF_A_NONE);
+    g_return_val_if_fail(line->line, QIF_A_NONE);
+
+    if (!qif_action_map)
+        build_action_map();
+
+    /* Duplicate the action and force it to lower case and strip any spaces */
+    action = g_utf8_strdown(line->line, -1);
+    g_strstrip(action);
+
+    result = g_hash_table_lookup(qif_action_map, action);
+    g_free(action);
+
+    if (!result)
+    {
+        /* XXX: pop up a dialog? */
+        PWARN("Unknown Action at line %d: %s.  Some transactions may be discarded",
+              line->lineno, line->line);
+        return QIF_A_NONE;
+    }
+
+    qaction = GPOINTER_TO_INT(result);
+    return qaction;
+}
+
+GList * qif_parse_acct_type(const char *str, gint lineno)
+{
+    GList *result;
+    char *type;
+
+    if (!qif_atype_map)
+        build_atype_map();
+
+    /* Duplicate the type and force it to lower case and strip any spaces */
+    type = g_utf8_strdown(str, -1);
+    g_strstrip(type);
+
+    result = g_hash_table_lookup(qif_atype_map, type);
+    g_free(type);
+
+    if (!result)
+    {
+        PWARN("Unknown account type at line %d: %s. ", lineno, str);
+        result = g_hash_table_lookup(qif_atype_map, "bank");
+        g_return_val_if_fail(result, NULL);
+    }
+
+    return result;
+}
+
+GList * qif_parse_acct_type_guess(QifType type)
+{
+    const char *atype = NULL;
+
+    switch (type)
+    {
+    case QIF_TYPE_BANK:
+        atype = "bank";
+        break;
+    case QIF_TYPE_CASH:
+        atype = "cash";
+        break;
+    case QIF_TYPE_CCARD:
+        atype = "ccard";
+        break;
+    case QIF_TYPE_INVST:
+        atype = "invst";
+        break;
+    case QIF_TYPE_PORT:
+        atype = "port";
+        break;
+    case QIF_TYPE_OTH_A:
+        atype = "oth a";
+        break;
+    case QIF_TYPE_OTH_L:
+        atype = "oth l";
+        break;
+    default:
+        return NULL;
+    }
+
+    return qif_parse_acct_type(atype, -1);
+}
+
+/***********************************************************************
+ * Parsing numbers and dates...
+ */
+
+typedef struct _parse_helper
+{
+    QifContext                ctx;
+
+    GncImportFormat        budget;
+    GncImportFormat        limit;
+    GncImportFormat        amount;
+    GncImportFormat        d_amount;
+    GncImportFormat        price;
+    GncImportFormat        shares;
+    GncImportFormat        commission;
+    GncImportFormat        date;
+} *parse_helper_t;
+
+#define QIF_PARSE_CHECK_NUMBER(str,help) { \
+        if (str) (help) = gnc_import_test_numeric((str), (help)); \
+}
+#define QIF_PARSE_PARSE_NUMBER(str,fmt,val) { \
+        if (str) gnc_import_parse_numeric((str), (fmt), (val)); \
+}
+
+static void
+qif_parse_check_account(gpointer key, gpointer val, gpointer data)
+{
+    parse_helper_t helper = data;
+    QifAccount acct = val;
+
+    QIF_PARSE_CHECK_NUMBER(acct->limitstr, helper->limit);
+    QIF_PARSE_CHECK_NUMBER(acct->budgetstr, helper->budget);
+}
+
+static void
+qif_parse_parse_account(gpointer key, gpointer val, gpointer data)
+{
+    parse_helper_t helper = data;
+    QifAccount acct = val;
+
+    QIF_PARSE_PARSE_NUMBER(acct->limitstr, helper->limit, &acct->limit);
+    QIF_PARSE_PARSE_NUMBER(acct->budgetstr, helper->budget, &acct->budget);
+}
+
+static void
+qif_parse_check_category(gpointer key, gpointer val, gpointer data)
+{
+    parse_helper_t helper = data;
+    QifCategory cat = val;
+
+    QIF_PARSE_CHECK_NUMBER(cat->budgetstr, helper->budget);
+}
+
+static void
+qif_parse_parse_category(gpointer key, gpointer val, gpointer data)
+{
+    parse_helper_t helper = data;
+    QifCategory cat = val;
+
+    QIF_PARSE_PARSE_NUMBER(cat->budgetstr, helper->budget, &cat->budget);
+}
+
+static void
+qif_parse_check_txn(gpointer val, gpointer data)
+{
+    parse_helper_t helper = data;
+    QifTxn txn = val;
+    QifSplit split;
+    QifInvstTxn itxn;
+    GList *node;
+
+    /* Check the date */
+    helper->date = gnc_import_test_date(txn->datestr, helper->date);
+
+    /* If this is an investment transaction, then all the info is in
+     * the invst_info.  Otherwise it's all in the splits.
+     */
+    itxn = txn->invst_info;
+    if (itxn)
+    {
+        QIF_PARSE_CHECK_NUMBER(itxn->amountstr, helper->amount);
+        QIF_PARSE_CHECK_NUMBER(itxn->d_amountstr, helper->d_amount);
+        QIF_PARSE_CHECK_NUMBER(itxn->pricestr, helper->price);
+        QIF_PARSE_CHECK_NUMBER(itxn->sharesstr, helper->shares);
+        QIF_PARSE_CHECK_NUMBER(itxn->commissionstr, helper->commission);
+
+    }
+    else
+    {
+        split = txn->default_split;
+        node = txn->splits;
+        do
+        {
+            QIF_PARSE_CHECK_NUMBER(split->amountstr, helper->amount);
+
+            if (node)
+            {
+                split = node->data;
+                node = node->next;
+            }
+            else
+                split = NULL;
+        }
+        while (split);
+    }
+}
+
+static void
+qif_parse_parse_txn(gpointer val, gpointer data)
+{
+    parse_helper_t helper = data;
+    QifTxn txn = val;
+    QifSplit split;
+    QifInvstTxn itxn;
+    GList *node;
+
+    /* Parse the date */
+    gnc_import_parse_date(txn->datestr, helper->date, &txn->date);
+
+    /* If this is an investment transaction, then all the info is in
+     * the invst_info.  Otherwise it's all in the splits.
+     */
+    itxn = txn->invst_info;
+    if (itxn)
+    {
+        QIF_PARSE_PARSE_NUMBER(itxn->amountstr, helper->amount, &itxn->amount);
+        QIF_PARSE_PARSE_NUMBER(itxn->d_amountstr, helper->d_amount, &itxn->d_amount);
+        QIF_PARSE_PARSE_NUMBER(itxn->pricestr, helper->price, &itxn->price);
+        QIF_PARSE_PARSE_NUMBER(itxn->sharesstr, helper->shares, &itxn->shares);
+        QIF_PARSE_PARSE_NUMBER(itxn->commissionstr, helper->commission,
+                               &itxn->commission);
+
+        qif_invst_txn_setup_splits(helper->ctx, txn);
+
+    }
+    else
+    {
+        split = txn->default_split;
+        node = txn->splits;
+        do
+        {
+            QIF_PARSE_PARSE_NUMBER(split->amountstr, helper->amount, &split->amount);
+
+            if (node)
+            {
+                split = node->data;
+                node = node->next;
+            }
+            else
+                split = NULL;
+        }
+        while (split);
+
+        qif_txn_setup_splits(txn);
+    }
+}
+
+void
+qif_parse_all(QifContext ctx, gpointer arg)
+{
+    struct _parse_helper helper;
+
+    helper.ctx = ctx;
+
+    /* PARSE ACCOUNTS */
+
+    /* First, figure out the formats */
+    helper.limit = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
+    helper.budget = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
+    qif_object_map_foreach(ctx, QIF_O_ACCOUNT, qif_parse_check_account, &helper);
+
+    /* Make sure it's not ambiguous */
+    if (helper.limit & (helper.limit - 1)) helper.limit = GNCIF_NUM_PERIOD;
+    if (helper.budget & (helper.budget - 1)) helper.budget = GNCIF_NUM_PERIOD;
+
+    /* Now convert the numbers */
+    qif_object_map_foreach(ctx, QIF_O_ACCOUNT, qif_parse_parse_account, &helper);
+
+    /* PARSE CATEGORIES */
+
+    helper.budget = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
+    qif_object_map_foreach(ctx, QIF_O_CATEGORY, qif_parse_check_category, &helper);
+
+    /* make sure it's not ambiguous */
+    if (helper.budget & (helper.budget - 1)) helper.budget = GNCIF_NUM_PERIOD;
+
+    /* Now convert the numbers */
+    qif_object_map_foreach(ctx, QIF_O_CATEGORY, qif_parse_parse_category, &helper);
+
+    /* PARSE TRANSACTIONS */
+    helper.amount = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
+    helper.d_amount = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
+    helper.price = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
+    helper.shares = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
+    helper.commission = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
+    helper.date = GNCIF_DATE_MDY | GNCIF_DATE_DMY | GNCIF_DATE_YMD | GNCIF_DATE_YDM;
+
+    qif_object_list_foreach(ctx, QIF_O_TXN, qif_parse_check_txn, &helper);
+
+    /* check/fix ambiguities */
+    if (helper.amount & (helper.amount - 1)) helper.amount = GNCIF_NUM_PERIOD;
+    if (helper.d_amount & (helper.d_amount - 1)) helper.d_amount = GNCIF_NUM_PERIOD;
+    if (helper.price & (helper.price - 1)) helper.price = GNCIF_NUM_PERIOD;
+    if (helper.shares & (helper.shares - 1)) helper.shares = GNCIF_NUM_PERIOD;
+    if (helper.commission & (helper.commission - 1))
+        helper.commission = GNCIF_NUM_PERIOD;
+
+    if (helper.date & (helper.date - 1))
+    {
+        helper.date = gnc_import_choose_fmt(_("The Date format is ambiguous.  "
+                                              "Please choose the correct format."),
+                                            helper.date, arg);
+    }
+
+    /* now parse it.. */
+    qif_object_list_foreach(ctx, QIF_O_TXN, qif_parse_parse_txn, &helper);
+}
+
+typedef struct
+{
+    QifContext        ctx;
+    GList *        list;
+    const char*        type;
+} qif_merge_t;
+
+static void
+qif_merge_accts(gpointer key, gpointer value, gpointer data)
+{
+    qif_merge_t *merge = data;
+    QifAccount acct = value;
+
+    /* Merge into the context.  Remember items moved into the parent */
+    if (qif_account_merge(merge->ctx, acct) == acct)
+        merge->list = g_list_prepend(merge->list, acct->name);
+}
+
+static void
+qif_merge_cats(gpointer key, gpointer value, gpointer data)
+{
+    qif_merge_t *merge = data;
+    QifCategory cat = value;
+
+    /* Merge into the context.  Remember items moved into the parent */
+    if (qif_cat_merge(merge->ctx, cat) == cat)
+        merge->list = g_list_prepend(merge->list, cat->name);
+}
+
+static void
+qif_merge_classes(gpointer key, gpointer value, gpointer data)
+{
+    qif_merge_t *merge = data;
+    QifClass qclass = value;
+
+    /* Merge into the context.  Remember items moved into the parent */
+    if (qif_class_merge(merge->ctx, qclass) == qclass)
+        merge->list = g_list_prepend(merge->list, qclass->name);
+}
+
+static void
+qif_merge_secs(gpointer key, gpointer value, gpointer data)
+{
+    qif_merge_t *merge = data;
+    QifSecurity sec = value;
+
+    /* Merge into the context.  Remember items moved into the parent */
+    if (qif_security_merge(merge->ctx, sec) == sec)
+        merge->list = g_list_prepend(merge->list, sec->name);
+}
+
+static void
+qif_merge_del(gpointer obj, gpointer data)
+{
+    qif_merge_t *merge = data;
+    const char *name = obj;
+
+    qif_object_map_remove(merge->ctx, merge->type, name);
+}
+
+static void
+qif_massage_split(QifSplit split, QifContext ctx)
+{
+    const char *type = QIF_O_CATEGORY;
+    char *name;
+
+    if (split->cat.obj)
+    {
+        if (split->cat_is_acct)
+        {
+            type = QIF_O_ACCOUNT;
+            name = split->cat.acct->name;
+        }
+        else
+            name = split->cat.cat->name;
+
+        split->cat.obj = qif_object_map_lookup(ctx, type, name);
+    }
+
+    if (split->cat_class)
+    {
+        split->cat_class = (QifClass) qif_object_map_lookup(ctx, QIF_O_CLASS,
+                           split->cat_class->name);
+    }
+}
+
+static void
+qif_massage_itxn(QifInvstTxn itxn, QifContext ctx)
+{
+    const char *type = QIF_O_CATEGORY;
+    char *name;
+
+    if (itxn->far_cat.obj)
+    {
+        if (itxn->far_cat_is_acct)
+        {
+            type = QIF_O_ACCOUNT;
+            name = itxn->far_cat.acct->name;
+        }
+        else
+            name = itxn->far_cat.cat->name;
+
+        itxn->far_cat.obj = qif_object_map_lookup(ctx, type, name);
+    }
+}
+
+static void
+qif_massage_txn(gpointer obj, gpointer data)
+{
+    QifTxn txn = obj;
+    QifContext ctx = data;
+    QifSplit split;
+    GList *node;
+
+    if (txn->from_acct)
+        txn->from_acct = (QifAccount) qif_object_map_lookup(ctx, QIF_O_ACCOUNT,
+                         txn->from_acct->name);
+
+    if (txn->invst_info)
+        qif_massage_itxn(txn->invst_info, ctx);
+
+    if (txn->default_split)
+        qif_massage_split(txn->default_split, ctx);
+
+    for (node = txn->splits; node; node = node->next)
+    {
+        split = node->data;
+        qif_massage_split(split, ctx);
+    }
+}
+
+void
+qif_parse_merge_files(QifContext ctx)
+{
+    GList *node;
+    GList *accts = NULL;
+    GList *cats = NULL;
+    GList *classes = NULL;
+    GList *securities = NULL;
+    QifContext fctx;
+
+    qif_merge_t merge;
+
+    g_return_if_fail(ctx);
+
+    /* Make sure each of the "file" contexts have been parsed.
+     * note that we don't care about OUR context -- we can run this
+     * process multiple times safely.
+     */
+    for (node = ctx->files; node; node = node->next)
+    {
+        fctx = node->data;
+        g_return_if_fail(fctx->parsed);
+    }
+
+
+    /* Iterate over each file.  Merge the Accounts, Categories, Classes,
+     * Securities, and Transactions into the top-level context.  Be sure
+     * to re-point all Transaction/Split category/class/account pointers
+     * to the new top-level item.  Then be sure to remove the
+     * "duplicated" items so we don't double-free (as we don't refcount,
+     * either).
+     */
+    for (node = ctx->files; node; node = node->next)
+    {
+        fctx = node->data;
+
+        /* Merge accts, categories, classes, and securities */
+
+        merge.ctx = ctx;
+        merge.list = NULL;
+        qif_object_map_foreach(fctx, QIF_O_ACCOUNT, qif_merge_accts, &merge);
+        accts = merge.list;
+
+        merge.list = NULL;
+        qif_object_map_foreach(fctx, QIF_O_CATEGORY, qif_merge_cats, &merge);
+        cats = merge.list;
+
+        merge.list = NULL;
+        qif_object_map_foreach(fctx, QIF_O_CLASS, qif_merge_classes, &merge);
+        classes = merge.list;
+
+        merge.list = NULL;
+        qif_object_map_foreach(fctx, QIF_O_SECURITY, qif_merge_secs, &merge);
+        securities = merge.list;
+
+
+        /* repoint the transactions to the merged context data */
+        qif_object_list_foreach(fctx, QIF_O_TXN, qif_massage_txn, ctx);
+
+
+        /* then remove from the file context objects referenced in the top context */
+        merge.ctx = fctx;
+        merge.type = QIF_O_ACCOUNT;
+        g_list_foreach(accts, qif_merge_del, &merge);
+        g_list_free(accts);
+
+        merge.type = QIF_O_CATEGORY;
+        g_list_foreach(cats, qif_merge_del, &merge);
+        g_list_free(cats);
+
+        merge.type = QIF_O_CLASS;
+        g_list_foreach(classes, qif_merge_del, &merge);
+        g_list_free(classes);
+
+        merge.type = QIF_O_SECURITY;
+        g_list_foreach(securities, qif_merge_del, &merge);
+        g_list_free(securities);
+
+    }
+
+    /* We've been parsed */
+    ctx->parsed = TRUE;
+}
diff --git a/gnucash/import-export/qif/qif-parse.h b/gnucash/import-export/qif/qif-parse.h
new file mode 100644
index 0000000..11d439c
--- /dev/null
+++ b/gnucash/import-export/qif/qif-parse.h
@@ -0,0 +1,50 @@
+/*
+ * qif-parse.h -- routines for parsing pieces of a QIF file
+ *
+ * Written By:	Derek Atkins  <derek at ihtfp.com>
+ * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ */
+
+#ifndef QIF_PARSE_H
+#define QIF_PARSE_H
+
+#include "qif-import.h"
+
+void qif_register_handler(QifType type, QifHandler handler);
+void qif_parse_bangtype(QifContext ctx, const char *line);
+
+gboolean
+qif_parse_split_category(const char* str,
+                         char** cat, gboolean *cat_is_acct, char** cat_class,
+                         char** miscx_cat, gboolean *miscx_cat_is_acct,
+                         char **miscx_class);
+
+gboolean qif_parse_numeric(QifLine line, gnc_numeric *num);
+QifRecnFlag qif_parse_cleared(QifLine line);
+QifAction qif_parse_action(QifLine line);
+
+/* The caller should never destroy this list */
+GList * qif_parse_acct_type(const char *str, gint lineno);
+GList * qif_parse_acct_type_guess(QifType type);
+
+/* Parse all objects */
+void qif_parse_all(QifContext ctx, gpointer ui_args);
+
+#endif /* QIF_PARSE_H */
diff --git a/gnucash/import-export/qif/test/CMakeLists.txt b/gnucash/import-export/qif/test/CMakeLists.txt
new file mode 100644
index 0000000..d874ca3
--- /dev/null
+++ b/gnucash/import-export/qif/test/CMakeLists.txt
@@ -0,0 +1,18 @@
+
+set(QIF_TEST_INCLUDE_DIRS
+  ${CMAKE_BINARY_DIR}/common
+  ${CMAKE_SOURCE_DIR}/gnucash/import-export/qif
+  ${CMAKE_SOURCE_DIR}/libgnucash/engine
+  ${CMAKE_SOURCE_DIR}/common/test-core
+  ${GLIB2_INCLUDE_DIRS}
+)
+set(QIF_TEST_LIBS gncmod-qif test-core)
+
+if (FALSE)
+  # Tests for this directory are not run.
+  gnc_add_test(test-link-qif test-link.c QIF_TEST_INCLUDE_DIRS QIF_TEST_LIBS)
+  gnc_add_test(test-qif test-qif.c QIF_TEST_INCLUDE_DIRS QIF_TEST_LIBS
+    GNC_TEST_FILES=${CMAKE_CURRENT_SOURCE_DIR}/test-files)
+endif()
+
+set_dist_list(test_qif_DIST CMakeLists.txt test-link.c test-qif.c test-files/test-1-bank-txn.qif)
diff --git a/gnucash/import-export/qif/test/test-files/test-1-bank-txn.qif b/gnucash/import-export/qif/test/test-files/test-1-bank-txn.qif
new file mode 100644
index 0000000..59592d0
--- /dev/null
+++ b/gnucash/import-export/qif/test/test-files/test-1-bank-txn.qif
@@ -0,0 +1,6 @@
+!Type:Bank
+D2003/01/27
+T123.45
+PTest Payee
+LTest Category
+^
diff --git a/gnucash/import-export/qif/test/test-link.c b/gnucash/import-export/qif/test/test-link.c
new file mode 100644
index 0000000..fd55d42
--- /dev/null
+++ b/gnucash/import-export/qif/test/test-link.c
@@ -0,0 +1,28 @@
+/********************************************************************\
+ * This program is free software; you can redistribute it and/or    *
+ * modify it under the terms of the GNU General Public License as   *
+ * published by the Free Software Foundation; either version 2 of   *
+ * the License, or (at your option) any later version.              *
+ *                                                                  *
+ * This program is distributed in the hope that it will be useful,  *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
+ * GNU General Public License for more details.                     *
+ *                                                                  *
+ * You should have received a copy of the GNU General Public License*
+ * along with this program; if not, contact:                        *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
+ *                                                                  *
+\********************************************************************/
+
+#include "qif-import.h"
+
+int
+main(int argc, char *argv[])
+{
+    qif_context_new();
+    return 0;
+}
diff --git a/gnucash/import-export/qif/test/test-qif.c b/gnucash/import-export/qif/test/test-qif.c
new file mode 100644
index 0000000..c331bef
--- /dev/null
+++ b/gnucash/import-export/qif/test/test-qif.c
@@ -0,0 +1,110 @@
+/*
+ * test-qif.c -- Test the QIF Import routines.
+ *
+ * Created by:	Derek Atkins <derek at ihtfp.com>
+ * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ */
+
+#include <glib.h>
+#include <libguile.h>
+
+#include "gnc-module.h"
+#include "qif-import.h"
+#include "qif-import-p.h"	/* Let's test some internal stuff, too */
+
+#include "test-stuff.h"
+
+/* XXX */
+extern void qif_object_init(void);
+
+static QifContext
+test_qif_load_file(QifContext ctx, const char *filename,
+                   gint txn_count, gint acct_count, gboolean needs_acct)
+{
+    QifContext file;
+
+    printf("qif loading \"%s\"...\n", filename);
+    file = qif_file_new(ctx, filename);
+    do_test(file != NULL, "failed to read file");
+    if (!file) return NULL;
+
+    do_test(qif_object_list_count(file, QIF_O_TXN) == txn_count,
+            "Transaction count didn't match");
+    do_test(qif_object_map_count(file, QIF_O_ACCOUNT) == acct_count,
+            "Account count didn't match");
+    do_test(qif_file_needs_account(file) == needs_acct,
+            "Needs account flad didn't match");
+
+    return file;
+}
+
+static void
+test_qif(void)
+{
+    QifContext ctx, file;
+    char *filename;
+    const char *location = g_getenv("GNC_TEST_FILES");
+    int i;
+
+    ctx = qif_context_new();
+    do_test(ctx != NULL, "failed to create the qif context");
+    if (!ctx) return;
+
+    if (!location)
+        location = "test-files";
+
+    for (i = 0; i < 1; i++)
+    {
+        filename = g_strdup_printf("%s/%s", location, "test-1-bank-txn.qif");
+        file = test_qif_load_file(ctx, filename, 1, 0, TRUE);
+        g_free(filename);
+        if (!file) continue;
+
+        if (qif_file_needs_account(file))
+            qif_file_set_default_account(file, "test-1-bank-txn");
+
+        do_test(qif_file_needs_account(file) == FALSE,
+                "'Needs account' flag not cleared properly");
+
+        do_test(qif_file_parse(file, NULL) == QIF_E_OK,
+                "file failed to parse.");
+    }
+
+    qif_context_destroy(ctx);
+
+    success("QIF test successful");
+}
+
+static void
+main_helper(void *closure, int argc, char **argv)
+{
+    qif_object_init();		/* XXX:FIXME */
+    test_qif();
+    print_test_results();
+    exit(get_rv());
+}
+
+int
+main(int argc, char **argv)
+{
+    scm_boot_guile(argc, argv, main_helper, NULL);
+    return 0;
+}
+
diff --git a/po/POTFILES.in b/po/POTFILES.in
index b8cc820..426b3f2 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -347,6 +347,9 @@ gnucash/import-export/ofx/gnc-ofx-import.c
 gnucash/import-export/ofx/gnc-ofx-kvp.c
 gnucash/import-export/ofx/gnc-plugin-ofx.c
 gnucash/import-export/ofx/gschemas/org.gnucash.dialogs.import.ofx.gschema.xml.in
+gnucash/import-export/qif/qif-context.c
+gnucash/import-export/qif/qif-file.c
+gnucash/import-export/qif/qif-objects.c
 gnucash/import-export/qif-imp/assistant-qif-import.c
 gnucash/import-export/qif-imp/dialog-account-picker.c
 gnucash/import-export/qif-imp/gncmod-qif-import.c
@@ -483,7 +486,8 @@ gnucash/report/standard-reports/general-journal.scm
 gnucash/report/standard-reports/general-ledger.scm
 gnucash/report/standard-reports/income-gst-statement.scm
 gnucash/report/standard-reports/income-statement.scm
-gnucash/report/standard-reports/net-charts.scm
+gnucash/report/standard-reports/net-barchart.scm
+gnucash/report/standard-reports/net-linechart.scm
 gnucash/report/standard-reports/portfolio.scm
 gnucash/report/standard-reports/price-scatter.scm
 gnucash/report/standard-reports/register.scm
@@ -571,6 +575,7 @@ libgnucash/backend/sql/gnc-sql-result.cpp
 libgnucash/backend/sql/gnc-tax-table-sql.cpp
 libgnucash/backend/sql/gnc-transaction-sql.cpp
 libgnucash/backend/sql/gnc-vendor-sql.cpp
+libgnucash/backend/xml/.#gnc-invoice-xml-v2.cpp
 libgnucash/backend/xml/gnc-account-xml-v2.cpp
 libgnucash/backend/xml/gnc-address-xml-v2.cpp
 libgnucash/backend/xml/gnc-backend-xml.cpp

commit edd439a05e1468b1f55d0df7c05441b67aac8787
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Jun 15 09:30:31 2018 -0700

    Remove abandoned C-language QIF implementation.

diff --git a/gnucash/import-export/CMakeLists.txt b/gnucash/import-export/CMakeLists.txt
index 3b52ed8..cca8359 100644
--- a/gnucash/import-export/CMakeLists.txt
+++ b/gnucash/import-export/CMakeLists.txt
@@ -10,7 +10,6 @@ add_subdirectory(csv-imp)
 add_subdirectory(customer-import)
 add_subdirectory(log-replay)
 add_subdirectory(ofx)
-add_subdirectory(qif)
 add_subdirectory(qif-imp)
 
 
diff --git a/gnucash/import-export/qif-imp/assistant-qif-import.c b/gnucash/import-export/qif-imp/assistant-qif-import.c
index 21f3e74..e424182 100644
--- a/gnucash/import-export/qif-imp/assistant-qif-import.c
+++ b/gnucash/import-export/qif-imp/assistant-qif-import.c
@@ -59,6 +59,7 @@
 #include "gnc-prefs.h"
 #include "gnc-ui.h"
 #include "guile-mappings.h"
+#include <gfec.h>
 
 #include "swig-runtime.h"
 
@@ -1067,6 +1068,12 @@ gnc_ui_qif_import_commodity_update(QIFImportWindow * wind)
     }
 }
 
+static void
+_gfec_error_handler(const char *message)
+{
+    PERR("qif-import:qif-to-gnc-undo encountered an error: %s", message);
+}
+
 
 /****************************************************************
  * gnc_ui_qif_import_convert_undo
@@ -1082,7 +1089,7 @@ gnc_ui_qif_import_convert_undo(QIFImportWindow * wind)
     gnc_set_busy_cursor(NULL, TRUE);
 
     /* Undo the conversion. */
-    scm_call_1(undo, wind->imported_account_tree);
+    gfec_apply(undo, wind->imported_account_tree, _gfec_error_handler);
 
     /* There's no imported account tree any more. */
     scm_gc_unprotect_object(wind->imported_account_tree);
diff --git a/gnucash/import-export/qif-imp/qif-to-gnc.scm b/gnucash/import-export/qif-imp/qif-to-gnc.scm
index 5f7e3f1..442cb47 100644
--- a/gnucash/import-export/qif-imp/qif-to-gnc.scm
+++ b/gnucash/import-export/qif-imp/qif-to-gnc.scm
@@ -910,7 +910,9 @@
     ;; this is the grind loop.  Go over every unmarked transaction in
     ;; the candidate-xtns list.
     (let xtn-loop ((xtns candidate-xtns))
-      (if (and (not (qif-xtn:mark (car xtns)))
+      (if (and (and
+                (and far-acct-name near acct-name)
+                (not (qif-xtn:mark (car xtns))))
                (string=? (qif-xtn:from-acct (car xtns)) far-acct-name))
           (begin
             (set! how
diff --git a/gnucash/import-export/qif/CMakeLists.txt b/gnucash/import-export/qif/CMakeLists.txt
deleted file mode 100644
index 608e14c..0000000
--- a/gnucash/import-export/qif/CMakeLists.txt
+++ /dev/null
@@ -1,43 +0,0 @@
-
-#Tests for this directory are not run.
-add_subdirectory(test)
-
-set(qif_SOURCES
-  qif-context.c
-  qif-defaults.c
-  qif-file.c
-  qif-objects.c
-  qif-parse.c
-)
-
-# Add dependency on config.h
-set_source_files_properties (${qif_SOURCES} PROPERTIES OBJECT_DEPENDS ${CONFIG_H})
-
-set(qif_noinst_HEADERS
-  qif-file.h
-  qif-defaults.h
-  qif-import-p.h
-  qif-import.h
-  qif-objects.h
-  qif-objects-p.h
-  qif-parse.h
-)
-
-add_library(gncmod-qif ${qif_noinst_HEADERS} ${qif_SOURCES})
-
-target_link_libraries(gncmod-qif gncmod-generic-import gncmod-engine ${GLIB2_LDFLAGS})
-
-target_compile_definitions(gncmod-qif PRIVATE -DG_LOG_DOMAIN=\"gnc.import.qif\")
-
-if (APPLE)
-  set_target_properties (gncmod-qif PROPERTIES INSTALL_NAME_DIR "${CMAKE_INSTALL_FULL_LIBDIR}/gnucash")
-endif()
-
-install(TARGETS gncmod-qif
-  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/gnucash
-  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/gnucash
-  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
-# No headers to install.
-
-set_local_dist(qif_DIST_local CMakeLists.txt ${qif_SOURCES} ${qif_noinst_HEADERS})
-set(qif_DIST ${qif_DIST_local} ${test_qif_DIST} PARENT_SCOPE)
diff --git a/gnucash/import-export/qif/qif-context.c b/gnucash/import-export/qif/qif-context.c
deleted file mode 100644
index 17fa472..0000000
--- a/gnucash/import-export/qif/qif-context.c
+++ /dev/null
@@ -1,417 +0,0 @@
-/*
- * qif-context.c -- create/destroy QIF Contexts
- *
- * Written By:	Derek Atkins <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <glib.h>
-
-#include "qif-import-p.h"
-#include "qif-objects-p.h"
-
-static void qif_object_map_get_helper(gpointer key, gpointer value, gpointer listp);
-
-QifContext
-qif_context_new(void)
-{
-    QifContext ctx = g_new0(struct _QifContext, 1);
-
-    ctx->object_lists = g_hash_table_new(g_str_hash, g_str_equal);
-    ctx->object_maps = g_hash_table_new(g_str_hash, g_str_equal);
-
-    return ctx;
-}
-
-void
-qif_context_destroy(QifContext ctx)
-{
-    GList *node, *temp;
-    QifContext fctx;
-
-    if (!ctx) return;
-
-    /* First, try to destroy all the children contexts */
-    for (node = ctx->files; node; node = temp)
-    {
-        fctx = node->data;
-        temp = node->next;
-        qif_context_destroy(fctx);
-    }
-
-    /* ok, at this point we're actually destroying this context. */
-
-    /* force the end of record */
-    if (ctx->handler && ctx->handler->end)
-        ctx->handler->end(ctx);
-
-    /* destroy the state objects */
-    qif_object_list_destroy(ctx);
-    qif_object_map_destroy(ctx);
-
-    /* Remove us from our parent context */
-    if (ctx->parent)
-        ctx->parent->files = g_list_remove(ctx->parent->files, ctx);
-
-    g_free(ctx->filename);
-
-    g_assert(ctx->files == NULL);
-    g_free(ctx);
-}
-
-static GList *
-qif_context_get_foo_helper(QifContext ctx, GFunc get_helper)
-{
-    GHashTable *ht;
-    GList *node, *list = NULL;
-    QifContext fctx;
-
-    g_return_val_if_fail(ctx, NULL);
-    g_return_val_if_fail(ctx->parsed, NULL);
-    g_return_val_if_fail(get_helper, NULL);
-
-    ht = g_hash_table_new(g_direct_hash, g_direct_equal);
-
-    for (node = ctx->files; node; node = node->next)
-    {
-        fctx = node->data;
-        qif_object_list_foreach(fctx, QIF_O_TXN, get_helper, ht);
-    }
-
-    g_hash_table_foreach(ht, qif_object_map_get_helper, &list);
-    g_hash_table_destroy(ht);
-
-    return list;
-}
-
-static void
-qif_get_accts_helper(gpointer obj, gpointer htp)
-{
-    QifTxn txn = obj;
-    QifSplit split;
-    GHashTable *ht = htp;
-    GList *node;
-
-    if (txn->from_acct)
-        g_hash_table_insert(ht, txn->from_acct, txn->from_acct);
-
-    /* The default_split is using the from_acct, so we can ignore it */
-
-    for (node = txn->splits; node; node = node->next)
-    {
-        split = node->data;
-        if (split->cat.obj && split->cat_is_acct)
-            g_hash_table_insert(ht, split->cat.acct, split->cat.acct);
-    }
-}
-
-GList *
-qif_context_get_accounts(QifContext ctx)
-{
-    return qif_context_get_foo_helper(ctx, qif_get_accts_helper);
-}
-
-static void
-qif_get_cats_helper(gpointer obj, gpointer htp)
-{
-    QifTxn txn = obj;
-    QifSplit split;
-    GHashTable *ht = htp;
-    GList *node;
-
-    /* default_split uses from_acct, so no categories */
-
-    for (node = txn->splits; node; node = node->next)
-    {
-        split = node->data;
-        if (split->cat.obj && !split->cat_is_acct)
-            g_hash_table_insert(ht, split->cat.cat, split->cat.cat);
-    }
-}
-
-GList *
-qif_context_get_categories(QifContext ctx)
-{
-    return qif_context_get_foo_helper(ctx, qif_get_cats_helper);
-}
-
-/*****************************************************************************/
-
-/*
- * Insert and remove a QifObject from the Object Maps in this Qif Context
- */
-
-gint
-qif_object_map_count(QifContext ctx, const char *type)
-{
-    GHashTable *ht;
-
-    g_return_val_if_fail(ctx, 0);
-    g_return_val_if_fail(ctx->object_maps, 0);
-    g_return_val_if_fail(type, 0);
-
-    ht = g_hash_table_lookup(ctx->object_maps, type);
-    if (!ht)
-        return 0;
-
-    return g_hash_table_size(ht);
-}
-
-void
-qif_object_map_foreach(QifContext ctx, const char *type, GHFunc func, gpointer arg)
-{
-    GHashTable *ht;
-
-    g_return_if_fail(ctx);
-    g_return_if_fail(ctx->object_maps);
-    g_return_if_fail(type);
-
-    ht = g_hash_table_lookup(ctx->object_maps, type);
-    if (ht)
-        g_hash_table_foreach(ht, func, arg);
-}
-
-void
-qif_object_map_insert(QifContext ctx, const char *key, QifObject obj)
-{
-    GHashTable *ht;
-
-    g_return_if_fail(ctx);
-    g_return_if_fail(ctx->object_maps);
-    g_return_if_fail(key);
-    g_return_if_fail(obj);
-    g_return_if_fail(obj->type);
-
-    ht = g_hash_table_lookup(ctx->object_maps, obj->type);
-    if (!ht)
-    {
-        ht = g_hash_table_new(g_str_hash, g_str_equal);
-        g_assert(ht);
-        g_hash_table_insert(ctx->object_maps, (gpointer)obj->type, ht);
-    }
-
-    g_hash_table_insert(ht, (gpointer)key, obj);
-}
-
-void
-qif_object_map_remove(QifContext ctx, const char *type, const char *key)
-{
-    GHashTable *ht;
-
-    g_return_if_fail(ctx);
-    g_return_if_fail(ctx->object_maps);
-    g_return_if_fail(type);
-    g_return_if_fail(key);
-
-    ht = g_hash_table_lookup(ctx->object_maps, type);
-    if (!ht) return;
-
-    g_hash_table_remove(ht, key);
-}
-
-QifObject
-qif_object_map_lookup(QifContext ctx, const char *type, const char *key)
-{
-    GHashTable *ht;
-
-    g_return_val_if_fail(ctx, NULL);
-    g_return_val_if_fail(ctx->object_maps, NULL);
-    g_return_val_if_fail(type, NULL);
-    g_return_val_if_fail(key, NULL);
-
-    ht = g_hash_table_lookup(ctx->object_maps, type);
-    if (!ht) return NULL;
-
-    return g_hash_table_lookup(ht, key);
-}
-
-/* This GList _SHOULD_ be freed by the caller */
-
-static void
-qif_object_map_get_helper(gpointer key, gpointer value, gpointer arg)
-{
-    GList **listp = arg;
-    g_return_if_fail(listp);
-
-    *listp = g_list_prepend(*listp, value);
-}
-
-GList *
-qif_object_map_get(QifContext ctx, const char *type)
-{
-    GHashTable *ht;
-    GList *list = NULL;
-
-    g_return_val_if_fail(ctx, NULL);
-    g_return_val_if_fail(ctx->object_maps, NULL);
-    g_return_val_if_fail(type, NULL);
-
-    ht = g_hash_table_lookup(ctx->object_maps, type);
-    if (!ht)
-        return NULL;
-
-    g_hash_table_foreach(ht, qif_object_map_get_helper, &list);
-
-    return list;
-}
-
-static gboolean
-qif_object_map_remove_each(gpointer key, gpointer value, gpointer arg)
-{
-    QifObject obj = value;
-    obj->destroy(obj);
-    return TRUE;
-}
-
-static gboolean
-qif_object_map_remove_all(gpointer key, gpointer value, gpointer arg)
-{
-    GHashTable *ht = value;
-
-    g_hash_table_foreach_remove(ht, qif_object_map_remove_each, NULL);
-    g_hash_table_destroy(ht);
-    return TRUE;
-}
-
-void qif_object_map_destroy(QifContext ctx)
-{
-    g_return_if_fail(ctx);
-    g_return_if_fail(ctx->object_maps);
-
-    g_hash_table_foreach_remove(ctx->object_maps, qif_object_map_remove_all, NULL);
-    g_hash_table_destroy(ctx->object_maps);
-}
-
-/*****************************************************************************/
-
-/*
- * Insert and remove a QifObject from the Object Lists in this Qif Context
- */
-
-void
-qif_object_list_reverse(QifContext ctx, const char *type)
-{
-    GList *list;
-
-    g_return_if_fail(ctx);
-    g_return_if_fail(ctx->object_lists);
-    g_return_if_fail(type);
-
-    list = qif_object_list_get(ctx, type);
-    list = g_list_reverse(list);
-    g_hash_table_insert(ctx->object_lists, (gpointer)type, list);
-}
-
-gint
-qif_object_list_count(QifContext ctx, const char *type)
-{
-    GList *list;
-
-    g_return_val_if_fail(ctx, 0);
-    g_return_val_if_fail(ctx->object_lists, 0);
-    g_return_val_if_fail(type, 0);
-
-    list = g_hash_table_lookup(ctx->object_lists, type);
-    return g_list_length(list);
-}
-
-void
-qif_object_list_foreach(QifContext ctx, const char *type, GFunc func, gpointer arg)
-{
-    GList *list;
-
-    g_return_if_fail(ctx);
-    g_return_if_fail(ctx->object_lists);
-    g_return_if_fail(type);
-
-    list = qif_object_list_get(ctx, type);
-    g_list_foreach(list, func, arg);
-}
-
-void
-qif_object_list_insert(QifContext ctx, QifObject obj)
-{
-    GList *list;
-
-    g_return_if_fail(ctx);
-    g_return_if_fail(ctx->object_lists);
-    g_return_if_fail(obj);
-    g_return_if_fail(obj->type && *obj->type);
-
-    list = g_hash_table_lookup(ctx->object_lists, obj->type);
-    list = g_list_prepend(list, obj);
-    g_hash_table_insert(ctx->object_lists, (gpointer)obj->type, list);
-}
-
-void
-qif_object_list_remove(QifContext ctx, QifObject obj)
-{
-    GList *list;
-
-    g_return_if_fail(ctx);
-    g_return_if_fail(ctx->object_lists);
-    g_return_if_fail(obj);
-    g_return_if_fail(obj->type && *obj->type);
-
-    list = g_hash_table_lookup(ctx->object_lists, obj->type);
-    list = g_list_remove(list, obj);
-    g_hash_table_insert(ctx->object_lists, (gpointer)obj->type, list);
-}
-
-GList *
-qif_object_list_get(QifContext ctx, const char *type)
-{
-    g_return_val_if_fail(ctx, NULL);
-    g_return_val_if_fail(ctx->object_lists, NULL);
-    g_return_val_if_fail(type, NULL);
-
-    return g_hash_table_lookup(ctx->object_lists, type);
-}
-
-static gboolean
-qif_object_list_remove_all(gpointer key, gpointer value, gpointer arg)
-{
-    GList *list = value;
-    GList *node;
-    QifObject obj;
-
-    for (node = list; node; node = node->next)
-    {
-        obj = node->data;
-        obj->destroy(obj);
-    }
-
-    g_list_free(list);
-    return TRUE;
-}
-
-void
-qif_object_list_destroy(QifContext ctx)
-{
-    g_return_if_fail(ctx);
-    g_return_if_fail(ctx->object_lists);
-
-    g_hash_table_foreach_remove(ctx->object_lists, qif_object_list_remove_all, NULL);
-    g_hash_table_destroy(ctx->object_lists);
-}
diff --git a/gnucash/import-export/qif/qif-defaults.c b/gnucash/import-export/qif/qif-defaults.c
deleted file mode 100644
index e29c141..0000000
--- a/gnucash/import-export/qif/qif-defaults.c
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * qif-defaults.c -- QIF Defaults -- default accounts...
- *
- * Created by:	Derek Atkins <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <glib.h>
-#include <glib/gi18n.h>
-
-#include "gnc-helpers.h"
-#include "qif-import-p.h"
-#include "qif-objects-p.h"
-#include "qif-defaults.h"
-
-
-static GList *stock_list = NULL;
-static GList *ext_stock_list = NULL;
-static GList *income_list = NULL;
-static GList *expense_list = NULL;
-static GList *equity_list = NULL;
-
-#define RETURN_ACCT(c,n,l) { if (stock_list == NULL) acct_type_init(); \
-	return find_or_make_acct(c, n, l); \
-}
-
-static void
-acct_type_init(void)
-{
-    stock_list = qif_parse_acct_type("__stock__", -1);
-    ext_stock_list = qif_parse_acct_type("__extstock__", -1);
-    income_list = qif_parse_acct_type("__income__", -1);
-    expense_list = qif_parse_acct_type("__expense__", -1);
-    equity_list = qif_parse_acct_type("__equity__", -1);
-}
-
-QifAccount qif_default_equity_acct(QifContext ctx)
-{
-    char *name = g_strdup(_("Retained Earnings"));
-    RETURN_ACCT(ctx, name, equity_list);
-}
-
-QifAccount qif_default_margin_interest_acct(QifContext ctx)
-{
-    char *name = g_strdup_printf("%s%s%s", _("Margin Interest"),
-                                 gnc_get_account_separator_string(),
-                                 ctx->current_acct->name);
-    RETURN_ACCT(ctx, name, expense_list);
-}
-
-QifAccount qif_default_commission_acct(QifContext ctx)
-{
-    char *name = g_strdup_printf("%s%s%s", _("Commissions"),
-                                 gnc_get_account_separator_string(),
-                                 ctx->current_acct->name);
-    RETURN_ACCT(ctx, name, expense_list);
-}
-
-QifAccount qif_default_stock_acct(QifContext ctx, const char *security)
-{
-    char *name = g_strdup_printf("%s%s%s", ctx->current_acct->name,
-                                 gnc_get_account_separator_string(),
-                                 security);
-    RETURN_ACCT(ctx, name, stock_list);
-}
-
-QifAccount qif_default_cglong_acct(QifContext ctx, const char *security)
-{
-    char *name = g_strdup_printf("%s%s%s%s%s", _("Cap. gain (long)"),
-                                 gnc_get_account_separator_string(),
-                                 ctx->current_acct->name,
-                                 gnc_get_account_separator_string(),
-                                 security);
-    RETURN_ACCT(ctx, name, income_list);
-}
-
-QifAccount qif_default_cgmid_acct(QifContext ctx, const char *security)
-{
-    char *name = g_strdup_printf("%s%s%s%s%s", _("Cap. gain (mid)"),
-                                 gnc_get_account_separator_string(),
-                                 ctx->current_acct->name,
-                                 gnc_get_account_separator_string(),
-                                 security);
-    RETURN_ACCT(ctx, name, income_list);
-}
-
-QifAccount qif_default_cgshort_acct(QifContext ctx, const char *security)
-{
-    char *name = g_strdup_printf("%s%s%s%s%s", _("Cap. gain (short)"),
-                                 gnc_get_account_separator_string(),
-                                 ctx->current_acct->name,
-                                 gnc_get_account_separator_string(),
-                                 security);
-    RETURN_ACCT(ctx, name, income_list);
-}
-
-QifAccount qif_default_dividend_acct(QifContext ctx, const char *security)
-{
-    char *name = g_strdup_printf("%s%s%s%s%s", _("Dividends"),
-                                 gnc_get_account_separator_string(),
-                                 ctx->current_acct->name,
-                                 gnc_get_account_separator_string(),
-                                 security);
-    RETURN_ACCT(ctx, name, income_list);
-}
-
-QifAccount qif_default_interest_acct(QifContext ctx, const char *security)
-{
-    char *name = g_strdup_printf("%s%s%s%s%s", _("Interest"),
-                                 gnc_get_account_separator_string(),
-                                 ctx->current_acct->name,
-                                 gnc_get_account_separator_string(),
-                                 security);
-    RETURN_ACCT(ctx, name, income_list);
-}
-
-QifAccount qif_default_capital_return_acct(QifContext ctx, const char *security)
-{
-    char *name = g_strdup_printf("%s%s%s%s%s", _("Cap Return"),
-                                 gnc_get_account_separator_string(),
-                                 ctx->current_acct->name,
-                                 gnc_get_account_separator_string(),
-                                 security);
-    RETURN_ACCT(ctx, name, income_list);
-}
-
-QifAccount qif_default_equity_holding(QifContext ctx, const char *security)
-{
-    return qif_default_equity_acct(ctx);
-}
-
diff --git a/gnucash/import-export/qif/qif-defaults.h b/gnucash/import-export/qif/qif-defaults.h
deleted file mode 100644
index 30c6562..0000000
--- a/gnucash/import-export/qif/qif-defaults.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * qif-defaults.h -- QIF Defaults -- default accounts...
- *
- * Created by:	Derek Atkins <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#ifndef QIF_DEFAULTS_H
-#define QIF_DEFAULTS_H
-
-#include "qif-objects.h"
-#include "qif-import.h"
-
-QifAccount qif_default_equity_acct(QifContext ctx);
-QifAccount qif_default_equity_holding(QifContext ctx, const char *security);
-
-QifAccount qif_default_margin_interest_acct(QifContext ctx);
-QifAccount qif_default_commission_acct(QifContext ctx);
-QifAccount qif_default_stock_acct(QifContext ctx, const char *security);
-QifAccount qif_default_cglong_acct(QifContext ctx, const char *security);
-QifAccount qif_default_cgmid_acct(QifContext ctx, const char *security);
-QifAccount qif_default_cgshort_acct(QifContext ctx, const char *security);
-QifAccount qif_default_dividend_acct(QifContext ctx, const char *security);
-QifAccount qif_default_interest_acct(QifContext ctx, const char *security);
-QifAccount qif_default_capital_return_acct(QifContext ctx, const char *security);
-
-#endif /* QIF_DEFAULTS_H */
diff --git a/gnucash/import-export/qif/qif-file.c b/gnucash/import-export/qif/qif-file.c
deleted file mode 100644
index 775f0da..0000000
--- a/gnucash/import-export/qif/qif-file.c
+++ /dev/null
@@ -1,326 +0,0 @@
-/*
- * qif-file.c -- parse a QIF File into its pieces
- *
- * Written by:  Derek Atkins  <derek@@ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <glib.h>
-#include <glib/gstdio.h>
-#include <string.h>
-
-#include "gnc-engine.h"
-
-#include "qif-import-p.h"
-#include "qif-objects-p.h"
-
-static QofLogModule log_module = GNC_MOD_IMPORT;
-
-
-static QifLine
-qif_make_line(const char* buf, gint lineno)
-{
-    QifLine line;
-    g_return_val_if_fail(buf && *buf, NULL);
-
-    line = g_new0(struct _QifLine, 1);
-    line->type = *buf;
-    line->lineno = lineno;
-    line->line = g_strdup(buf + 1);
-
-    return line;
-}
-
-void
-qif_record_destroy(GList *record)
-{
-    GList *node;
-    QifLine line;
-
-    for (node = record; node; node = node->next)
-    {
-        line = node->data;
-        g_free(line->line);
-        g_free(line);
-    }
-
-    g_list_free(record);
-}
-
-/* This returns a record, which is a bunch of QifLines, ending
- * with a line with just a '^'.  If it finds a line that begins
- * with a !, then destroy the current record state, set the "found_bangtype",
- * and return NULL.
- */
-static GList *
-qif_make_record(QifContext ctx, char *buf, size_t bufsiz, gboolean *found_bangtype)
-{
-    GList *record = NULL;
-    QifLine line;
-
-    g_return_val_if_fail(ctx, NULL);
-    g_return_val_if_fail(buf, NULL);
-    g_return_val_if_fail(found_bangtype, NULL);
-
-    *found_bangtype = FALSE;
-
-    while (fgets(buf, bufsiz, ctx->fp) != NULL)
-    {
-
-        /* increment the line number */
-        ctx->lineno++;
-
-        /* strip start/end whitespace */
-        g_strstrip(buf);
-
-        /* if there is nothing left in the string, ignore it */
-        if (strlen(buf) == 0)
-            continue;
-
-        /* If this is a bangline, then set the flag, clear our state, and return NULL */
-        if (*buf == '!')
-        {
-            *found_bangtype = TRUE;
-            break;
-        }
-
-        /* See if this is an End of Record marker */
-        if (*buf == '^')
-        {
-            /* Yep.  If we've got a record then break and return ... */
-            if (record)
-                break;
-            /* ... otherwise just continue reading (i.e. ignore empty records) */
-            else
-                continue;
-        }
-
-        /* otherwise, add the line to the list */
-        line = qif_make_line(buf, ctx->lineno);
-        if (line)
-            record = g_list_prepend(record, line);
-
-        /* and continue... */
-    }
-
-    /* If we found a bangtype, destroy anything we've collected */
-    if (*found_bangtype)
-    {
-        if (record)
-            PERR("error loading file: incomplete record at line %d", ctx->lineno);
-
-        qif_record_destroy(record);
-        record = NULL;
-    }
-
-    return g_list_reverse(record);
-}
-
-/* read a qif file and parse it, line by line
- * return QIF_E_OK on success or some other QIF Error.
- */
-static QifError
-qif_read_file(QifContext ctx, FILE *f)
-{
-    char buf[BUFSIZ];
-    GList *record;
-    gboolean found_bang;
-    QifError err = QIF_E_OK;
-
-    g_return_val_if_fail(ctx, QIF_E_BADARGS);
-    g_return_val_if_fail(f, QIF_E_BADARGS);
-
-    ctx->fp = f;
-    ctx->lineno = -1;
-
-    do
-    {
-        found_bang = FALSE;
-        record = qif_make_record(ctx, buf, sizeof(buf), &found_bang);
-
-        /* If we got a record, process it */
-        if (record)
-        {
-            if (!ctx->handler || !ctx->handler->parse_record)
-            {
-                PERR("Trying to process QIF record without a handler at %d", ctx->lineno);
-            }
-            else
-            {
-                err = ctx->handler->parse_record(ctx, record);
-            }
-
-            /* Now destroy it; we don't need it anymore */
-            qif_record_destroy(record);
-        }
-
-        /* if we found a bangtype, process that */
-        if (found_bang)
-        {
-            g_assert(*buf == '!');
-
-            /* First, process the end of the last handler.  This could possibly
-             * merge items into the context or perform some other operation
-             */
-            if (ctx->handler && ctx->handler->end)
-            {
-                err = ctx->handler->end(ctx);
-                if (err != QIF_E_OK)
-                    break;
-            }
-
-            /* Now process the bangtype (stored in buf) to set the new handler */
-            qif_parse_bangtype(ctx, buf);
-        }
-
-    }
-    while ((record || found_bang) && err == QIF_E_OK);
-
-    /* Make sure to run any end processor */
-    if (err == QIF_E_OK && ctx->handler && ctx->handler->end)
-        err = ctx->handler->end(ctx);
-
-    if (err == QIF_E_OK)
-        qif_object_list_reverse(ctx, QIF_O_TXN);
-
-    return err;
-}
-
-static QifError
-qif_import_file(QifContext ctx, const char *filename)
-{
-    QifError err;
-    FILE *fp;
-
-    g_return_val_if_fail(ctx, QIF_E_BADARGS);
-    g_return_val_if_fail(filename, QIF_E_BADARGS);
-    g_return_val_if_fail(*filename, QIF_E_BADARGS);
-
-    /* Open the file */
-    fp = g_fopen(filename, "r");
-    if (fp == NULL)
-        return QIF_E_NOFILE;
-
-    ctx->filename = g_strdup(filename);
-
-    /* read the file */
-    err = qif_read_file(ctx, fp);
-
-    /* close the file */
-    fclose(fp);
-
-    return err;
-}
-
-
-QifContext
-qif_file_new(QifContext ctx, const char *filename)
-{
-    QifContext fctx;
-
-    g_return_val_if_fail(ctx, NULL);
-    g_return_val_if_fail(filename, NULL);
-
-    fctx = qif_context_new();
-
-    /* we should assume that we've got a bank account... just in case.. */
-    qif_parse_bangtype(fctx, "!type:bank");
-
-    /* Open the file */
-    if (qif_import_file(fctx, filename) != QIF_E_OK)
-    {
-        qif_context_destroy(fctx);
-        fctx = NULL;
-    }
-
-    /* Return the new context */
-    if (fctx)
-    {
-        ctx->files = g_list_prepend(ctx->files, fctx);
-        fctx->parent = ctx;
-
-        /* Make sure the file gets merged into the parent */
-        ctx->parsed = FALSE;
-    }
-
-    return fctx;
-}
-
-QifError
-qif_file_parse(QifContext ctx, gpointer ui_args)
-{
-    g_return_val_if_fail(ctx, QIF_E_BADARGS);
-    g_return_val_if_fail(!qif_file_needs_account(ctx), QIF_E_BADSTATE);
-
-    qif_parse_all(ctx, ui_args);
-    ctx->parsed = TRUE;
-
-    return QIF_E_OK;
-}
-
-gboolean
-qif_file_needs_account(QifContext ctx)
-{
-    g_return_val_if_fail(ctx, FALSE);
-
-    return ((ctx->parse_flags & QIF_F_TXN_NEEDS_ACCT) ||
-            (ctx->parse_flags & QIF_F_ITXN_NEEDS_ACCT));
-}
-
-const char *
-qif_file_filename(QifContext ctx)
-{
-    g_return_val_if_fail(ctx, NULL);
-    return ctx->filename;
-}
-
-static void
-set_txn_acct(gpointer obj, gpointer arg)
-{
-    QifTxn txn = obj;
-    QifAccount acct = arg;
-
-    if (!txn->from_acct)
-        txn->from_acct = acct;
-}
-
-void
-qif_file_set_default_account(QifContext ctx, const char *acct_name)
-{
-    QifAccount acct;
-
-    g_return_if_fail(ctx);
-    g_return_if_fail(acct_name);
-
-    if (! qif_file_needs_account(ctx)) return;
-
-    acct = find_or_make_acct(ctx, g_strdup(acct_name),
-                             qif_parse_acct_type_guess(ctx->parse_type));
-
-    qif_object_list_foreach(ctx, QIF_O_TXN, set_txn_acct, acct);
-
-    qif_clear_flag(ctx->parse_flags, QIF_F_TXN_NEEDS_ACCT);
-    qif_clear_flag(ctx->parse_flags, QIF_F_ITXN_NEEDS_ACCT);
-}
diff --git a/gnucash/import-export/qif/qif-file.h b/gnucash/import-export/qif/qif-file.h
deleted file mode 100644
index c4b85f1..0000000
--- a/gnucash/import-export/qif/qif-file.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/* qif-import-p.h -- a QIF Importer module (private headers)
- *
- * Written By:	Derek Atkins <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#ifndef QIF_FILE_H
-#define QIF_FILE_H
-
-struct _QifLine
-{
-    char		type;
-    gint		lineno;
-    char *	line;
-};
-
-void qif_record_destroy(GList *record);
-
-#endif /* QIF_FILE_H */
diff --git a/gnucash/import-export/qif/qif-import-p.h b/gnucash/import-export/qif/qif-import-p.h
deleted file mode 100644
index 96c6314..0000000
--- a/gnucash/import-export/qif/qif-import-p.h
+++ /dev/null
@@ -1,100 +0,0 @@
-/* qif-import-p.h -- a QIF Importer module (private headers)
- *
- * Written By:	Derek Atkins <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#ifndef QIF_IMPORT_P_H
-#define QIF_IMPORT_P_H
-
-#include "qif-import.h"
-#include "qif-objects.h"
-#include "qif-parse.h"
-#include "qif-file.h"
-
-#include <stdio.h>
-
-struct _QifHandler
-{
-    void		(*init)(QifContext ctx);
-    QifError	(*parse_record)(QifContext ctx, GList *record);
-    QifError	(*end)(QifContext ctx);
-};
-
-struct _QifContext
-{
-    /* The parent context */
-    QifContext	parent;
-
-    /* file information */
-    char *	filename;
-    FILE *	fp;
-    gint		lineno;
-
-    /* This describes what we are parsing right now */
-    QifType	parse_type;
-    QifHandler	handler;
-    gpointer	parse_state;
-
-    /* A bunch of flags for the current handler */
-    gint		parse_flags;
-    gboolean	parsed;
-
-    /* The current and "opening balance" account */
-    QifAccount	current_acct;
-    QifAccount	opening_bal_acct;
-
-    /* HashTable of Maps of data objects */
-    GHashTable *	object_maps;
-
-    /* HashTable of Lists of data objects */
-    GHashTable *	object_lists;
-
-    /* List of files */
-    GList *files;
-};
-
-/* Object Maps */
-gint qif_object_map_count(QifContext ctx, const char *type);
-void qif_object_map_foreach(QifContext ctx, const char *type,
-                            GHFunc func, gpointer arg);
-void qif_object_map_insert(QifContext ctx, const char *key, QifObject obj);
-void qif_object_map_remove(QifContext ctx, const char *type, const char *key);
-QifObject qif_object_map_lookup(QifContext ctx, const char *type, const char *key);
-void qif_object_map_destroy(QifContext ctx);
-/* GList _SHOULD_ be freed by the caller */
-GList * qif_object_map_get(QifContext ctx, const char *type);
-
-/* Object Lists */
-void qif_object_list_reverse(QifContext ctx, const char *type);
-gint qif_object_list_count(QifContext ctx, const char *type);
-void qif_object_list_foreach(QifContext ctx, const char *type,
-                             GFunc func, gpointer arg);
-void qif_object_list_insert(QifContext ctx, QifObject obj);
-void qif_object_list_remove(QifContext ctx, QifObject obj);
-void qif_object_list_destroy(QifContext ctx);
-/* GList should NOT be freed by the caller */
-GList *qif_object_list_get(QifContext ctx, const char *type);
-
-/* Set and clear flags in bit-flags */
-#define qif_set_flag(i,f) (i |= f)
-#define qif_clear_flag(i,f) (i &= ~f)
-
-#endif /* QIF_IMPORT_P_H */
diff --git a/gnucash/import-export/qif/qif-import.h b/gnucash/import-export/qif/qif-import.h
deleted file mode 100644
index 9602029..0000000
--- a/gnucash/import-export/qif/qif-import.h
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * qif-import.h -- a QIF Import module
- *
- * Written By:	Derek Atkins <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#ifndef QIF_IMPORT_H
-#define QIF_IMPORT_H
-
-#include <stdio.h>
-#include "qof.h"
-
-typedef enum
-{
-    QIF_TYPE_BANK = 1,
-    QIF_TYPE_CASH,
-    QIF_TYPE_CCARD,
-    QIF_TYPE_INVST,
-    QIF_TYPE_PORT,
-    QIF_TYPE_OTH_A,
-    QIF_TYPE_OTH_L,
-    QIF_TYPE_CLASS,
-    QIF_TYPE_CAT,
-    QIF_TYPE_SECURITY,
-    QIF_ACCOUNT,
-    QIF_AUTOSWITCH,
-    QIF_CLEAR_AUTOSWITCH
-} QifType;
-
-/* Make sure this patches */
-#define QIF_TYPE_MAX QIF_CLEAR_AUTOSWITCH
-
-typedef struct _QifHandler *QifHandler;
-typedef struct _QifContext *QifContext;
-typedef struct _QifLine *QifLine;
-
-/* Qif Flags */
-#define QIF_F_IGNORE_ACCOUNTS	(1 << 0)
-#define QIF_F_TXN_NEEDS_ACCT	(1 << 1)
-#define QIF_F_ITXN_NEEDS_ACCT	(1 << 2)
-
-/* Qif Reconciled Flag */
-typedef enum
-{
-    QIF_R_NO = 0,
-    QIF_R_CLEARED,
-    QIF_R_RECONCILED,
-    QIF_R_BUDGETED,
-} QifRecnFlag;
-
-/* Qif Errors */
-
-typedef enum
-{
-    QIF_E_OK = 0,
-    QIF_E_INTERNAL,
-    QIF_E_BADSTATE,
-    QIF_E_BADARGS,
-    QIF_E_NOFILE,
-} QifError;
-
-
-/* Qif (investment?) Actions */
-typedef enum
-{
-    QIF_A_NONE = 0,
-    QIF_A_BUY,
-    QIF_A_BUYX,
-    QIF_A_CGLONG,
-    QIF_A_CGLONGX,
-    QIF_A_CGMID,
-    QIF_A_CGMIDX,
-    QIF_A_CGSHORT,
-    QIF_A_CGSHORTX,
-    QIF_A_DIV,
-    QIF_A_DIVX,
-    QIF_A_EXERCISE,
-    QIF_A_EXERCISEX,
-    QIF_A_EXPIRE,
-    QIF_A_GRANT,
-    QIF_A_INTINC,
-    QIF_A_INTINCX,
-    QIF_A_MARGINT,
-    QIF_A_MARGINTX,
-    QIF_A_MISCEXP,
-    QIF_A_MISCEXPX,
-    QIF_A_MISCINC,
-    QIF_A_MISCINCX,
-    QIF_A_REINVDIV,
-    QIF_A_REINVINT,
-    QIF_A_REINVLG,
-    QIF_A_REINVMD,
-    QIF_A_REINVSG,
-    QIF_A_REINVSH,
-    QIF_A_REMINDER,
-    QIF_A_RTRNCAP,
-    QIF_A_RTRNCAPX,
-    QIF_A_SELL,
-    QIF_A_SELLX,
-    QIF_A_SHRSIN,
-    QIF_A_SHRSOUT,
-    QIF_A_STKSPLIT,
-    QIF_A_VEST,
-    QIF_A_XIN,
-    QIF_A_XOUT,
-} QifAction;
-
-/* Public API Functions */
-
-/* Create a QIF Import Context */
-QifContext qif_context_new(void);
-void qif_context_destroy(QifContext ctx);
-
-/* Open and read a QIF File.  You must pass in the parent
- * context; it will return the child (file) context
- */
-QifContext qif_file_new(QifContext ctx, const char* filename);
-
-/* Does a qif-file need a default QIF account? */
-gboolean qif_file_needs_account(QifContext ctx);
-
-/* Return the filename of the QIF file */
-const char * qif_file_filename(QifContext ctx);
-
-/* Provide a default QIF Account for the QIF File */
-void qif_file_set_default_account(QifContext ctx, const char *acct_name);
-
-/* Parse the QIF File */
-QifError qif_file_parse(QifContext ctx, gpointer ui_arg);
-
-/* Merge all the qif-files from the children and into the context */
-void qif_parse_merge_files(QifContext ctx);
-
-/* Obtain the list of USED QifAccounts and QifCategories.  Finds all
- * references from the transactions in the QifContext.  The returned
- * GList must be freed by the caller.
- */
-GList *qif_context_get_accounts(QifContext ctx);
-GList *qif_context_get_categories(QifContext ctx);
-
-#endif /* QIF_IMPORT_H */
diff --git a/gnucash/import-export/qif/qif-objects-p.h b/gnucash/import-export/qif/qif-objects-p.h
deleted file mode 100644
index dbd3aa4..0000000
--- a/gnucash/import-export/qif/qif-objects-p.h
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * qif-objects-p.h -- Private header: QIF objects for the QIF importer
- *
- * Written By:	Derek Atkins <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#ifndef QIF_OBJECTS_P_H
-#define QIF_OBJECTS_P_H
-
-#include "qof.h"
-
-#include "qif-import.h"
-#include "qif-objects.h"
-
-struct _QifAccount
-{
-    struct _QifObject obj;
-
-    char *	name;
-    char *	desc;
-
-    char *	limitstr;
-    gnc_numeric	limit;
-
-    char *	budgetstr;
-    gnc_numeric	budget;
-
-    GList *	type_list;
-
-};
-
-struct _QifCategory
-{
-    struct _QifObject obj;
-
-    char *	name;
-    char *	desc;
-    char *	taxclass;
-
-    gboolean	taxable;
-    gboolean	expense;
-    gboolean	income;
-
-    char *	budgetstr;
-    gnc_numeric	budget;
-
-};
-
-struct _QifClass
-{
-    struct _QifObject obj;
-
-    char *	name;
-    char *	desc;
-    char *	taxdesig;
-
-};
-
-struct _QifSecurity
-{
-    struct _QifObject obj;
-
-    char *	name;
-    char *	symbol;
-    char *	type;
-
-};
-
-struct _QifTxn
-{
-    struct _QifObject obj;
-
-    QifType	txn_type;
-
-    char *	datestr;
-    Timespec	date;
-
-    char *	payee;
-    char *	address;
-    char *	num;
-
-    QifRecnFlag	cleared;
-
-    /* Investment info */
-    QifInvstTxn	invst_info;
-
-    /* The default_split is the default (forward) part of the QIF transaction */
-    QifSplit	default_split;
-
-    /* The current_split (if any) defines the current "qif split" we are handling */
-    QifSplit	current_split;
-
-    /* The "from" account */
-    QifAccount	from_acct;
-
-    /* The list of splits for this txn */
-    GList *	splits;
-
-};
-
-struct _QifSplit
-{
-    char *	memo;
-
-    char *	amountstr;
-    gnc_numeric	amount;
-    gnc_numeric	value;
-
-    char *	catstr;
-
-    /* parsed category/account info */
-
-    union
-    {
-        QifObject	obj;
-        QifCategory	cat;
-        QifAccount	acct;
-    } cat;
-    gboolean	cat_is_acct;
-    QifClass	cat_class;
-
-};
-
-struct _QifInvstTxn
-{
-    QifAction	action;
-
-    gnc_numeric	amount;
-    gnc_numeric	d_amount;
-    gnc_numeric	price;
-    gnc_numeric	shares;
-    gnc_numeric	commission;
-
-    char *	amountstr;
-    char *	d_amountstr;
-    char *	pricestr;
-    char *	sharesstr;
-    char *	commissionstr;
-
-    char *	security;
-    char *	catstr;
-
-    union
-    {
-        QifObject	obj;
-        QifCategory	cat;
-        QifAccount	acct;
-    } far_cat;
-    gboolean	far_cat_is_acct;
-};
-
-/* to be run after parsing all the dates and amounts */
-void qif_txn_setup_splits(QifTxn txn);
-void qif_invst_txn_setup_splits(QifContext ctx, QifTxn txn);
-
-#endif /* QIF_OBJECTS_P_H */
diff --git a/gnucash/import-export/qif/qif-objects.c b/gnucash/import-export/qif/qif-objects.c
deleted file mode 100644
index 0fad928..0000000
--- a/gnucash/import-export/qif/qif-objects.c
+++ /dev/null
@@ -1,1468 +0,0 @@
-/*
- * qif-objects.c -- Objects for the QIF Importer
- *
- * Written by:	Derek Atkins  <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <glib.h>
-#include <string.h>
-#include "Account.h"
-
-#include "gnc-engine.h"
-
-#include "qif-import-p.h"
-#include "qif-objects-p.h"
-#include "qif-defaults.h"
-
-static QofLogModule log_module = GNC_MOD_IMPORT;
-
-/* create a new object of type t, with type-string type and
- * destroy function dest.  Requires 'obj' to be set.
- */
-#define qif_object_new(t,typ,dest) ({ \
-	obj = (QifObject) g_new0(t, 1); \
-	obj->type = typ; \
-	obj->destroy = dest; \
-	obj; \
-})
-
-/* Save the string from this "line".  Also:
- * - make sure we're not over-writing anything.
- * - make sure the 'line' object no longer references the string.
- */
-#define qif_save_str(var) { \
-	if (var) { \
-		PERR("duplicate found at line %d: %s", line->lineno, line->line); \
-		g_free(var); \
-	} \
-	(var) = line->line; \
-	line->line = NULL; \
-}
-
-/* QIF Account */
-static void
-qif_account_destroy(QifObject obj)
-{
-    QifAccount acct = (QifAccount) obj;
-
-    g_free(acct->name);
-    g_free(acct->desc);
-    g_free(acct->limitstr);
-    g_free(acct->budgetstr);
-
-    g_free(acct);
-};
-
-static QifAccount
-qif_account_new(void)
-{
-    QifObject obj;
-    QifAccount acct;
-
-    obj = qif_object_new(struct _QifAccount, QIF_O_ACCOUNT, qif_account_destroy);
-
-    acct = (QifAccount)obj;
-    acct->type_list = qif_parse_acct_type("bank", -1);
-
-    acct->limit = gnc_numeric_zero();
-    acct->budget = gnc_numeric_zero();
-    return acct;
-}
-
-/*
- * Merge acct into ctx.  If this account already exists in ctx then
- * merge in any new values from acct into the ctx version and return
- * the existing acct.  If the account does not already exist, then
- * insert it into the ctx and return it.
- */
-QifAccount
-qif_account_merge(QifContext ctx, QifAccount acct)
-{
-    QifAccount acct2 =
-        (QifAccount)qif_object_map_lookup(ctx, acct->obj.type, acct->name);
-
-    if (!acct2)
-    {
-        qif_object_map_insert(ctx, acct->obj.type, (QifObject)acct);
-        return acct;
-    }
-
-    /* obviously the name is the same, so don't worry about that */
-
-    if (!acct2->desc && acct->desc)
-        acct2->desc = g_strdup(acct->desc);
-
-    if (!acct2->type_list && acct->type_list)
-        acct2->type_list = acct->type_list;
-
-    if (!acct2->limitstr && acct->limitstr)
-    {
-        acct2->limitstr = g_strdup(acct->limitstr);
-        acct2->limit = acct->limit;
-    }
-
-    if (!acct2->budgetstr && acct->budgetstr)
-    {
-        acct2->budgetstr = g_strdup(acct->budgetstr);
-        acct2->budget = acct->budget;
-    }
-
-    return acct2;
-}
-
-static QifError
-qif_account_parse(QifContext ctx, GList *record)
-{
-    QifAccount acct, temp;
-    QifLine line;
-
-    g_return_val_if_fail(ctx, QIF_E_INTERNAL);
-    g_return_val_if_fail(record, QIF_E_BADSTATE);
-
-    acct = qif_account_new();
-
-    for (; record; record = record->next)
-    {
-        line = record->data;
-
-        switch (line->type)
-        {
-        case 'N':			/* N : account name */
-            qif_save_str(acct->name);
-            break;
-        case 'D': 			/* D : account description */
-            qif_save_str(acct->desc);
-            break;
-        case 'T':			/* T : account type */
-            acct->type_list = qif_parse_acct_type(line->line, line->lineno);
-            break;
-        case 'L':			/* L : account limit */
-            qif_save_str(acct->limitstr);
-            break;
-        case 'B':			/* B : account budget */
-            qif_save_str(acct->budgetstr);
-            break;
-        default:
-            PERR("Unknown QIF account data at line %d: %s", line->lineno, line->line);
-        }
-    }
-
-    /* Merge the account into the context */
-    temp = qif_account_merge(ctx, acct);
-    if (! (ctx->parse_flags & QIF_F_IGNORE_ACCOUNTS))
-        ctx->current_acct = temp;
-    if (temp != acct)
-        qif_account_destroy((QifObject)acct);
-
-    return QIF_E_OK;
-}
-
-/* QIF Category */
-static void
-qif_cat_destroy(QifObject obj)
-{
-    QifCategory cat = (QifCategory) obj;
-
-    g_free(cat->name);
-    g_free(cat->desc);
-    g_free(cat->taxclass);
-    g_free(cat->budgetstr);
-
-    g_free(cat);
-}
-
-static QifCategory
-qif_cat_new(void)
-{
-    QifObject obj;
-    QifCategory cat;
-
-    obj = qif_object_new(struct _QifCategory, QIF_O_CATEGORY, qif_cat_destroy);
-    cat = (QifCategory)obj;
-    cat->budget = gnc_numeric_zero();
-
-    return cat;
-}
-
-/*
- * Merge cat into ctx.  If this category already exists in ctx then
- * merge in any new values from cat into the ctx version and return
- * the existing cat.  If the category does not already exist, then
- * insert it into the ctx and return it.
- */
-QifCategory
-qif_cat_merge(QifContext ctx, QifCategory cat)
-{
-    QifCategory cat2 =
-        (QifCategory)qif_object_map_lookup(ctx, cat->obj.type, cat->name);
-
-    if (!cat2)
-    {
-        qif_object_map_insert(ctx, cat->obj.type, (QifObject)cat);
-        return cat;
-    }
-
-    /* obviously the name is the same, so don't worry about that */
-
-    if (!cat2->desc && cat->desc)
-        cat2->desc = g_strdup(cat->desc);
-
-    if (!cat2->taxclass && cat->taxclass)
-        cat2->taxclass = g_strdup(cat->taxclass);
-
-    cat2->taxable = (cat2->taxable || cat->taxable);
-    cat2->expense = (cat2->expense || cat->expense);
-    cat2->income = (cat2->income || cat->income);
-
-    if (!cat2->budgetstr && cat->budgetstr)
-    {
-        cat2->budgetstr = g_strdup(cat->budgetstr);
-        cat2->budget = cat->budget;
-    }
-
-    return cat2;
-}
-
-static QifError
-qif_cat_parse(QifContext ctx, GList *record)
-{
-    QifCategory cat;
-    QifLine line;
-
-    g_return_val_if_fail(ctx, QIF_E_INTERNAL);
-    g_return_val_if_fail(record, QIF_E_BADSTATE);
-
-    cat = qif_cat_new();
-
-    for (; record; record = record->next)
-    {
-        line = record->data;
-
-        switch (line->type)
-        {
-        case 'N':			/* N : category name */
-            qif_save_str(cat->name);
-            break;
-        case 'D':			/* D : category description */
-            qif_save_str(cat->desc);
-            break;
-        case 'T':			/* T : category is taxable? */
-            cat->taxable = TRUE;
-            break;
-        case 'E':			/* E : category is expense? */
-            cat->expense = TRUE;
-            break;
-        case 'I':			/* I : category is income? */
-            cat->income = TRUE;
-            break;
-        case 'R':			/* R : category taxclass XXX */
-            /* XXX: a number? */
-            qif_save_str(cat->taxclass);
-            break;
-        case 'B':			/* B : category budget */
-            qif_save_str(cat->budgetstr);
-            break;
-        default:
-            PERR("Unknown QIF category data at line %d: %s", line->lineno, line->line);
-        }
-    }
-
-    if (qif_cat_merge(ctx, cat) != cat)
-        qif_cat_destroy((QifObject)cat);
-
-    return QIF_E_OK;
-}
-
-/* QIF Class */
-static void
-qif_class_destroy(QifObject obj)
-{
-    QifClass qclass = (QifClass) obj;
-
-    g_free(qclass->name);
-    g_free(qclass->desc);
-    g_free(qclass->taxdesig);
-
-    g_free(qclass);
-}
-
-static QifClass
-qif_class_new()
-{
-    QifObject obj;
-
-    obj = qif_object_new(struct _QifClass, QIF_O_CLASS, qif_class_destroy);
-    return (QifClass)obj;
-}
-
-/*
- * Merge qclass into ctx.  If this class already exists in ctx then
- * merge in any new values from qclass into the ctx version and return
- * the existing qclass.  If the class does not already exist, then
- * insert it into the ctx and return it.
- */
-QifClass
-qif_class_merge(QifContext ctx, QifClass qclass)
-{
-    QifClass qclass2 =
-        (QifClass)qif_object_map_lookup(ctx, qclass->obj.type, qclass->name);
-
-    if (!qclass2)
-    {
-        qif_object_map_insert(ctx, qclass->obj.type, (QifObject)qclass);
-        return qclass;
-    }
-
-    /* obviously the name is the same, so don't worry about that */
-
-    if (!qclass2->desc && qclass->desc)
-        qclass2->desc = g_strdup(qclass->desc);
-
-    if (!qclass2->taxdesig && qclass->taxdesig)
-        qclass2->taxdesig = g_strdup(qclass->taxdesig);
-
-    return qclass2;
-}
-
-static QifError
-qif_class_parse(QifContext ctx, GList *record)
-{
-    QifClass qclass;
-    QifLine line;
-
-    g_return_val_if_fail(ctx, QIF_E_INTERNAL);
-    g_return_val_if_fail(record, QIF_E_BADSTATE);
-
-    qclass = qif_class_new();
-
-    for (; record; record = record->next)
-    {
-        line = record->data;
-
-        switch (line->type)
-        {
-        case 'N':			/* N : class name */
-            qif_save_str(qclass->name);
-            break;
-        case 'D':			/* D : class description */
-            qif_save_str(qclass->desc);
-            break;
-        case 'R':			/* R : Tax designator */
-            qif_save_str(qclass->taxdesig);
-            break;
-        default:
-            PERR("Unknown QIF class data at line %d: %s", line->lineno, line->line);
-        }
-    }
-
-    if (qif_class_merge(ctx, qclass) != qclass)
-        qif_class_destroy((QifObject)qclass);
-
-    return QIF_E_OK;
-}
-
-/* QIF Security Symbol */
-static void
-qif_security_destroy(QifObject obj)
-{
-    QifSecurity security = (QifSecurity) obj;
-
-    g_free(security->name);
-    g_free(security->symbol);
-    g_free(security->type);
-
-    g_free(security);
-}
-
-static QifSecurity
-qif_security_new()
-{
-    QifObject obj;
-
-    obj = qif_object_new(struct _QifSecurity, QIF_O_SECURITY, qif_security_destroy);
-    return (QifSecurity)obj;
-}
-
-/*
- * Merge security into ctx.  If this security already exists in ctx then
- * merge in any new values from security into the ctx version and return
- * the existing security.  If the security does not already exist, then
- * insert it into the ctx and return it.
- */
-QifSecurity
-qif_security_merge(QifContext ctx, QifSecurity security)
-{
-    QifSecurity security2 =
-        (QifSecurity)qif_object_map_lookup(ctx, security->obj.type, security->name);
-
-    if (!security2)
-    {
-        qif_object_map_insert(ctx, security->obj.type, (QifObject)security);
-        return security;
-    }
-
-    /* obviously the name is the same, so don't worry about that */
-
-    if (!security2->symbol && security->symbol)
-        security2->symbol = g_strdup(security->symbol);
-
-    if (!security2->type && security->type)
-        security2->type = g_strdup(security->type);
-
-    return security2;
-}
-
-static QifError
-qif_security_parse(QifContext ctx, GList *record)
-{
-    QifSecurity security;
-    QifLine line;
-
-    g_return_val_if_fail(ctx, QIF_E_INTERNAL);
-    g_return_val_if_fail(record, QIF_E_BADSTATE);
-
-    security = qif_security_new();
-
-    for (; record; record = record->next)
-    {
-        line = record->data;
-
-        switch (line->type)
-        {
-        case 'N':			/* N : security name */
-            qif_save_str(security->name);
-            break;
-        case 'S':			/* S : security symbol */
-            qif_save_str(security->symbol);
-            break;
-        case 'T':			/* T : security type */
-            qif_save_str(security->type);
-            break;
-        default:
-            PERR("Unknown QIF security data at line %d: %s", line->lineno, line->line);
-        }
-    }
-
-    if (qif_security_merge(ctx, security) != security)
-        qif_security_destroy((QifObject)security);
-
-    return QIF_E_OK;
-}
-
-/********************* TXN *********************/
-
-static QifSplit
-qif_split_new()
-{
-    QifSplit split = g_new0(struct _QifSplit, 1);
-
-    /* Initialize to 'zero' (even though they are not valid) */
-    split->amount = gnc_numeric_zero();
-    split->value = gnc_numeric_zero();
-
-    return split;
-}
-
-static void
-qif_split_destroy(QifSplit split)
-{
-    if (!split) return;
-
-    g_free(split->memo);
-    g_free(split->catstr);
-    g_free(split->amountstr);
-
-    g_free(split);
-}
-
-static QifSplit
-qif_split_copy(QifSplit split)
-{
-    QifSplit s = qif_split_new();
-
-    memcpy(s, split, sizeof(*s));
-    if (s->memo) s->memo = g_strdup(s->memo);
-    if (s->amountstr) s->amountstr = g_strdup(s->amountstr);
-    if (s->catstr) s->memo = g_strdup(s->catstr);
-
-    return s;
-}
-
-/* Forward declarations */
-static void qif_txn_invst_destroy(QifInvstTxn);
-
-/* QIF Transaction */
-
-static void
-qif_split_parse_category(QifContext ctx, QifSplit split)
-{
-    char *cat = NULL;
-    char *cat_class = NULL;
-    char *miscx_cat = NULL;
-    char *miscx_class = NULL;
-
-    gboolean miscx_is_acct;
-
-    static GList *types = NULL;
-
-    g_return_if_fail(ctx);
-    g_return_if_fail(split);
-    g_return_if_fail(split->cat.cat == NULL && split->cat_class == NULL);
-
-    if (qif_parse_split_category(split->catstr,
-                                 &cat, &split->cat_is_acct, &cat_class,
-                                 &miscx_cat, &miscx_is_acct, &miscx_class))
-    {
-        g_assert(cat);
-
-        if (split->cat_is_acct)
-        {
-            if (types == NULL)
-                types = qif_parse_acct_type("__any_bank__", -1);
-
-            split->cat.acct = find_or_make_acct(ctx, cat, types);
-
-        }
-        else
-            split->cat.cat = find_or_make_cat(ctx, cat);
-
-        if (cat_class)
-            split->cat_class = find_or_make_class(ctx, cat_class);
-
-        /* miscx isn't used in a normal transaction, so just ignore it */
-        if (miscx_cat)
-            g_free(miscx_cat);
-        if (miscx_class)
-            g_free(miscx_class);
-
-    }
-    else
-        PERR("Problem parsing split category: %s", split->catstr);
-}
-
-static void
-qif_txn_destroy(QifObject obj)
-{
-    QifTxn txn = (QifTxn) obj;
-    GList *node;
-    QifSplit split;
-
-    g_free(txn->datestr);
-    g_free(txn->payee);
-    g_free(txn->address);
-    g_free(txn->num);
-
-    if (txn->invst_info)
-        qif_txn_invst_destroy(txn->invst_info);
-
-    for (node = txn->splits; node; node = node->next)
-    {
-        split = node->data;
-        if (split == txn->default_split)
-            txn->default_split = NULL;
-        if (split == txn->current_split)
-            txn->current_split = NULL;
-
-        qif_split_destroy(split);
-    }
-
-    g_list_free(txn->splits);
-    qif_split_destroy(txn->default_split);
-    qif_split_destroy(txn->current_split);
-
-    g_free(txn);
-}
-
-static QifTxn
-qif_txn_new(void)
-{
-    QifObject obj;
-    QifTxn txn;
-
-    obj = qif_object_new(struct _QifTxn, "qif-txn", qif_txn_destroy);
-    txn = (QifTxn) obj;
-    txn->default_split = qif_split_new();
-
-    return txn;
-}
-
-static void
-qif_txn_init(QifContext ctx)
-{
-    qif_clear_flag(ctx->parse_flags, QIF_F_IGNORE_ACCOUNTS);
-    ctx->parse_state = NULL;
-}
-
-static gboolean
-qif_is_bad_numeric_string(const char* line)
-{
-    return (strncmp(line, "...", 3) == 0);
-}
-
-/*
- * this is called for the first transaction after each !Type: tag.
- *
- * if the first transaction after a !Type: tag has a payee of "Opening
- * Balance" or "Initial Balance", we have to massage the transaction a
- * little.  The meaning of an OB transaction is "transfer from Equity
- * to the account specified in the L line."  Idiomatically, ms-money
- * and some others use this transaction instead of an Account record
- * to specify "this" account (the from-account for all following
- * transactions), so we have to allow for that.
- *
- * Even if the payee isn't "Opening Balance", we if we have no default
- * from-account by this time we need to set one.  In that case we set
- * the default account based on the file name.
- *
- * If we DO know the account already, and this is a transfer to it,
- * it's also an opening balance regardless of the payee.
- *
- * In the end make sure that the context 'current account' is set.
- */
-static void
-qif_process_opening_balance_txn(QifContext ctx, QifTxn txn)
-{
-    QifSplit split = txn->default_split;
-    QifAccount cur_acct = NULL;	/* We know that ctx->current_acct is NULL */
-
-    g_return_if_fail(txn->invst_info == NULL);
-
-    if ((!cur_acct && txn->payee &&
-            (!strcasecmp(txn->payee, "Opening Balance") ||
-             !strcasecmp(txn->payee, "Initial Balance")) && split->cat_is_acct) ||
-            (cur_acct &&
-             ((split->cat_is_acct && !strcasecmp(split->cat.acct->name, cur_acct->name))
-              ||
-              (!split->cat_is_acct && !strcasecmp(split->cat.cat->name, cur_acct->name))))
-       )
-    {
-
-        /* This is an explicit "Opening Balance" transactions.  We need to
-         * change the "from account" to point to the equity account that
-         * the opening balance comes from...
-         */
-        if (split->cat_is_acct)
-            cur_acct = split->cat.acct;
-        else
-        {
-            g_assert(split->cat.cat);
-            cur_acct = find_or_make_acct(ctx, g_strdup(split->cat.cat->name),
-                                         qif_parse_acct_type_guess(txn->txn_type));
-            split->cat_is_acct = TRUE;
-        }
-        split->cat.acct = qif_default_equity_acct(ctx);
-    }
-
-    /*
-     * If we found an opening balance account then set up the context.
-     * If we didn't actually succeed in finding an account then
-     * set a flag so we can go back later and look for it.
-     */
-
-    if (cur_acct)
-    {
-        ctx->opening_bal_acct = cur_acct;
-        ctx->current_acct = cur_acct;
-    }
-    else
-        qif_set_flag(ctx->parse_flags, QIF_F_TXN_NEEDS_ACCT);
-}
-
-/* process all the splits in the transaction -- if this is a "split
- * transaction" then make sure the sum of all the amounts (including
- * the default split) does NOT equal zero -- if it does then we want
- * to reverse all the splits.  The "amount" should be the 'T' amount
- * from the txn.
- */
-static void
-qif_txn_fix_amounts(QifTxn txn, gnc_numeric amount)
-{
-    gnc_numeric sum = amount;
-    QifSplit split;
-    GList *node;
-
-    g_return_if_fail(txn);
-
-    /* No current_split, so this is NOT a split transaction. */
-    if (!txn->current_split) return;
-
-    /* Then add in every split in the split-list */
-    for (node = txn->splits; node; node = node->next)
-    {
-        split = node->data;
-        sum = gnc_numeric_add(sum, split->amount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
-    }
-
-    /* if the sum is not zero then reverse all the amounts in the split list */
-    if (!gnc_numeric_zero_p(sum))
-        for (node = txn->splits; node; node = node->next)
-        {
-            split = node->data;
-            split->amount = gnc_numeric_neg(split->amount);
-        }
-}
-
-static QifError
-qif_txn_parse(QifContext ctx, GList *record)
-{
-    QifTxn txn;
-    QifLine line;
-    GList *node;
-    QifSplit split;
-
-    g_return_val_if_fail(ctx, QIF_E_INTERNAL);
-    g_return_val_if_fail(record, QIF_E_BADSTATE);
-
-    txn = qif_txn_new();
-    txn->txn_type = ctx->parse_type;
-
-    for (; record; record = record->next)
-    {
-        line = record->data;
-
-        switch (line->type)
-        {
-        case 'D':			/* D : transaction date */
-            qif_save_str(txn->datestr);
-            break;
-        case 'P':			/* P : payee */
-            qif_save_str(txn->payee);
-            break;
-        case 'A':			/* A : address */
-            /* multiple 'A' lines are appended together with newlines */
-            if (txn->address)
-            {
-                char *tmp = txn->address;
-                txn->address = g_strconcat(tmp, "\n", line->line, NULL);
-                g_free(tmp);
-            }
-            else
-                qif_save_str(txn->address);
-            break;
-        case 'N':			/* N : check/transaction number */
-            qif_save_str(txn->num);
-            break;
-        case 'C':			/* C : transaction cleared flag */
-            txn->cleared = qif_parse_cleared(line);
-            break;
-        case 'L':			/* L : default split category */
-            if (!txn->current_split) qif_save_str(txn->default_split->catstr);
-            break;
-        case 'M':			/* M : default split memo */
-            if (!txn->current_split) qif_save_str(txn->default_split->memo);
-            break;
-        case 'T':			/* T : total transaction amount */
-            if (!txn->current_split && !qif_is_bad_numeric_string(line->line))
-                qif_save_str(txn->default_split->amountstr);
-            break;
-        case 'S':			/* S : split category */
-            /* This implies a quicken-style "split transaction", so we're mostly
-             * going to ignore the default_split except for internal verification.
-             */
-            txn->current_split = qif_split_new();
-            txn->splits = g_list_prepend(txn->splits, txn->current_split);
-            qif_save_str(txn->current_split->catstr);
-            break;
-        case 'E':			/* E : split memo */
-            if (txn->current_split)
-                qif_save_str(txn->current_split->memo);
-            break;
-        case '$':			/* split amount */
-            if (txn->current_split && !qif_is_bad_numeric_string(line->line))
-                qif_save_str(txn->current_split->amountstr);
-            break;
-        default:
-            PERR("Unknown QIF transaction data at line %d: %s", line->lineno, line->line);
-        }
-    }
-
-    /* If we have no date string then there is no reason to do anything else */
-    if (txn->datestr)
-    {
-        /* We delay processing the date and amount strings until later.. */
-
-        /* parse the category on each split */
-        for (node = txn->splits; node; node = node->next)
-        {
-            split = node->data;
-            if (split->catstr)
-                qif_split_parse_category(ctx, split);
-        }
-        /* ... including the default split */
-        if (txn->default_split->catstr)
-            qif_split_parse_category(ctx, txn->default_split);
-
-        /* if we don't have an account, then deal with the opening balance */
-        if (!ctx->current_acct)
-            qif_process_opening_balance_txn(ctx, txn);
-
-        /* Set the transaction's from account */
-        txn->from_acct = ctx->current_acct;
-
-        /* And add it to the process list */
-        ctx->parse_state = g_list_prepend(ctx->parse_state, txn);
-
-    }
-    else
-        /* no date?  Ignore this txn */
-        qif_txn_destroy((QifObject)txn);
-
-    return QIF_E_OK;
-}
-
-/* after we parse the amounts, fix up the transaction splits */
-void
-qif_txn_setup_splits(QifTxn txn)
-{
-    QifSplit split, this_split;
-    GList *node;
-    gnc_numeric total;
-
-    if (txn->splits)
-    {
-        /* We have a bunch of "far" splits -- maybe fix up the totals.. */
-        qif_txn_fix_amounts(txn, txn->default_split->amount);
-
-        /* Re-Compute the total for the "near" (default) split */
-        total = gnc_numeric_zero();
-        for (node = txn->splits; node; node = node->next)
-        {
-            split = node->data;
-            split->value = split->amount;
-            total = gnc_numeric_add(total, split->amount, 0, GNC_HOW_DENOM_LCD);
-        }
-
-        /* And re-set the default-split amount */
-        txn->default_split->amount = gnc_numeric_neg(total);
-
-    }
-    else
-    {
-        /* not a split txn.  Compute the "far" split by copying the "near"
-         *  split and then moving the 'near' split to the far split.
-         */
-
-        /* First make a copy of this transaction and move the copy to the 'near' */
-        split = txn->default_split;
-        this_split = qif_split_copy(split);
-        txn->default_split = this_split;
-
-        /* then adjust the 'far' txn */
-        split->amount = gnc_numeric_neg(split->amount);
-        split->value = split->amount;
-        txn->splits = g_list_prepend(txn->splits, split);
-    }
-
-    /* Set the default-split value from the default-split amount */
-    txn->default_split->value = txn->default_split->amount;
-}
-
-/* This is called when we're done processing an account.  We want
- * to merge the transactions in the "parse_state" into the Qif Context
- */
-static QifError
-qif_txn_end_acct(QifContext ctx)
-{
-    GList *node;
-    QifTxn txn;
-    gboolean txn_needs_acct;
-
-    g_return_val_if_fail(ctx, QIF_E_INTERNAL);
-
-    /* Return now if there is nothing to do. */
-    if (!ctx->parse_state) return QIF_E_OK;
-
-    /* Walk through the list of transactions.  First check if it
-     * needs a from-account; then add it to the context.
-     */
-
-    txn_needs_acct = (ctx->parse_flags & QIF_F_TXN_NEEDS_ACCT);
-
-    /* Invert the list so we're working in the right order */
-    ctx->parse_state = g_list_reverse(ctx->parse_state);
-
-    for (node = ctx->parse_state; node; node = node->next)
-    {
-        txn = node->data;
-
-        /* If we need a from account, then set it.. */
-        if (txn_needs_acct && ctx->opening_bal_acct && !txn->from_acct)
-            txn->from_acct = ctx->opening_bal_acct;
-
-        /* merge the txn into the context (prepends to the list) */
-        qif_object_list_insert(ctx, (QifObject)txn);
-    }
-
-    if (txn_needs_acct && ctx->opening_bal_acct)
-        qif_clear_flag(ctx->parse_flags, QIF_F_TXN_NEEDS_ACCT);
-
-    /* clean up our state */
-    g_list_free(ctx->parse_state);
-    ctx->parse_state = NULL;
-
-    return QIF_E_OK;
-}
-
-/* Extra info in an Investment Transaction */
-static QifInvstTxn
-qif_invst_txn_new(void)
-{
-    QifInvstTxn itxn = g_new0(struct _QifInvstTxn, 1);
-
-    itxn->amount = gnc_numeric_zero();
-    itxn->d_amount = gnc_numeric_zero();
-    itxn->price = gnc_numeric_zero();
-    itxn->shares = gnc_numeric_zero();
-    itxn->commission = gnc_numeric_zero();
-
-    return itxn;
-}
-
-static void
-qif_txn_invst_destroy(QifInvstTxn itxn)
-{
-    if (!itxn) return;
-
-    g_free(itxn->amountstr);
-    g_free(itxn->d_amountstr);
-    g_free(itxn->pricestr);
-    g_free(itxn->sharesstr);
-    g_free(itxn->commissionstr);
-    g_free(itxn->security);
-
-    g_free(itxn->catstr);
-
-    g_free(itxn);
-}
-
-static QifError
-qif_txn_invst_parse(QifContext ctx, GList *record)
-{
-    QifTxn txn;
-    QifInvstTxn itxn;
-    QifLine line;
-
-    g_return_val_if_fail(ctx, QIF_E_INTERNAL);
-    g_return_val_if_fail(record, QIF_E_BADSTATE);
-
-    txn = qif_txn_new();
-    txn->txn_type = ctx->parse_type;
-    itxn = qif_invst_txn_new();
-    txn->invst_info = itxn;
-
-    for (; record; record = record->next)
-    {
-        line = record->data;
-
-        switch (line->type)
-        {
-        case 'D':			/* D : transaction date */
-            qif_save_str(txn->datestr);
-            break;
-        case 'P':			/* P : txn payee */
-            qif_save_str(txn->payee);
-            break;
-        case 'N':			/* N : action */
-            itxn->action = qif_parse_action(line);
-            break;
-        case 'C':			/* C : cleared flag */
-            txn->cleared = qif_parse_cleared(line);
-            break;
-        case 'M':			/* M : memo */
-            if (!txn->current_split)
-                qif_save_str(txn->default_split->memo);
-            break;
-        case 'T':			/* T : total amount */
-            if (!qif_is_bad_numeric_string(line->line))
-                qif_save_str(itxn->amountstr);
-            break;
-        case '$':			/* $ : transfer amount */
-            if (!qif_is_bad_numeric_string(line->line))
-                qif_save_str(itxn->d_amountstr);
-            break;
-        case 'I':			/* I : share price */
-            qif_save_str(itxn->pricestr);
-            break;
-        case 'Q':			/* Q : number of shares */
-            qif_save_str(itxn->sharesstr);
-            break;
-        case 'Y':			/* Y : name of security */
-            qif_save_str(itxn->security);
-            break;
-        case 'O':			/* O : commission */
-            qif_save_str(itxn->commissionstr);
-            break;
-        case 'L':			/* L : category */
-            qif_save_str(itxn->catstr);
-            break;
-        default:
-            PERR("Unknown QIF Investment transaction data at line %d: %s",
-                 line->lineno, line->line);
-        }
-    }
-
-    /* If we have no date string then there is no reason to do anything else */
-    if (txn->datestr && itxn->action != QIF_A_NONE)
-    {
-
-        /* Make sure we've got a security name */
-        if (!itxn->security)
-            itxn->security = g_strdup("");	/* XXX */
-
-        /* if we don't have a from account, then mark the fact that
-         * we'll need one later.
-         */
-        if (ctx->current_acct)
-            txn->from_acct = ctx->current_acct;
-        else
-            qif_set_flag(ctx->parse_flags, QIF_F_ITXN_NEEDS_ACCT);
-
-        /* Add this transaction to the parse state for later processing */
-        ctx->parse_state = g_list_prepend(ctx->parse_state, txn);
-
-    }
-    else
-    {
-        /* no date?  Just destroy it */
-        qif_txn_destroy((QifObject)txn);
-    }
-
-    return QIF_E_OK;
-}
-
-
-void
-qif_invst_txn_setup_splits(QifContext ctx, QifTxn txn)
-{
-    QifInvstTxn itxn;
-    QifSplit near_split, far_split, comm_split;
-    QifAccount from_acct;
-
-    char *cat = NULL;
-    char *cat_class = NULL;
-    gboolean cat_is_acct = FALSE;
-    char *miscx = NULL;
-    char *miscx_class = NULL;
-    gboolean miscx_is_acct = FALSE;
-
-    /* Cached account-type lists */
-    static GList *bank_list = NULL;
-
-    gnc_numeric split_value;
-
-    g_return_if_fail(ctx);
-    g_return_if_fail(txn);
-    g_return_if_fail(txn->invst_info);
-
-    itxn = txn->invst_info;
-
-    /* Compute the share value, because we'll probably need it */
-    split_value = gnc_numeric_mul(itxn->shares, itxn->price, 0, GNC_HOW_DENOM_REDUCE);
-
-    /* Make sure that "amount" is a valid "transaction amount" */
-    if (!itxn->amountstr && itxn->d_amountstr)
-        itxn->amount = itxn->d_amount;
-
-    /* near and far splits..  for simplicity */
-    near_split = txn->default_split;
-    far_split = qif_split_new();
-    from_acct = txn->from_acct;
-
-    /* Parse the category string */
-    if (!qif_parse_split_category(itxn->catstr,
-                                  &cat, &cat_is_acct, &cat_class,
-                                  &miscx, &miscx_is_acct, &miscx_class))
-        PERR("Failure parsing category: %s", itxn->catstr);
-
-    /* Make sure we've got a cached list */
-    if (bank_list == NULL)
-        bank_list = qif_parse_acct_type("__any_bank__", -1);
-
-    /* find the NEAR account */
-
-    switch (itxn->action)
-    {
-    case QIF_A_BUY:
-    case QIF_A_BUYX:
-    case QIF_A_REINVDIV:
-    case QIF_A_REINVINT:
-    case QIF_A_REINVLG:
-    case QIF_A_REINVMD:
-    case QIF_A_REINVSG:
-    case QIF_A_REINVSH:
-    case QIF_A_SELL:
-    case QIF_A_SELLX:
-    case QIF_A_SHRSIN:
-    case QIF_A_SHRSOUT:
-    case QIF_A_STKSPLIT:
-        txn->from_acct = qif_default_stock_acct(ctx, itxn->security);
-        break;
-
-    case QIF_A_CGLONG:
-    case QIF_A_CGMID:
-    case QIF_A_CGSHORT:
-    case QIF_A_DIV:
-    case QIF_A_INTINC:
-    case QIF_A_MARGINT:
-    case QIF_A_MISCEXP:
-    case QIF_A_MISCINC:
-    case QIF_A_RTRNCAP:
-    case QIF_A_XIN:
-    case QIF_A_XOUT:
-        txn->from_acct = from_acct;
-        break;
-
-    case QIF_A_CGLONGX:
-    case QIF_A_CGMIDX:
-    case QIF_A_CGSHORTX:
-    case QIF_A_DIVX:
-    case QIF_A_INTINCX:
-    case QIF_A_MARGINTX:
-    case QIF_A_RTRNCAPX:
-        txn->from_acct = find_or_make_acct(ctx, cat, bank_list);
-        cat = NULL;
-        break;
-
-    case QIF_A_MISCEXPX:
-    case QIF_A_MISCINCX:
-        txn->from_acct = find_or_make_acct(ctx, miscx, bank_list);
-        miscx = NULL;
-        break;
-
-    default:
-        PERR("Unhandled Action: %d", itxn->action);
-        break;
-    }
-
-    /* find the FAR account */
-
-    itxn->far_cat_is_acct = TRUE;
-    switch (itxn->action)
-    {
-    case QIF_A_BUY:
-    case QIF_A_SELL:
-        itxn->far_cat.acct = from_acct;
-        break;
-
-    case QIF_A_BUYX:
-    case QIF_A_MISCEXP:
-    case QIF_A_MISCEXPX:
-    case QIF_A_MISCINC:
-    case QIF_A_MISCINCX:
-    case QIF_A_SELLX:
-    case QIF_A_XIN:
-    case QIF_A_XOUT:
-        itxn->far_cat.cat = find_or_make_cat(ctx, cat);
-        itxn->far_cat_is_acct = FALSE;
-        cat = NULL;
-        break;
-
-    case QIF_A_CGLONG:
-    case QIF_A_CGLONGX:
-    case QIF_A_REINVLG:
-        itxn->far_cat.acct = qif_default_cglong_acct(ctx, itxn->security);
-        break;
-
-    case QIF_A_CGMID:
-    case QIF_A_CGMIDX:
-    case QIF_A_REINVMD:
-        itxn->far_cat.acct = qif_default_cgmid_acct(ctx, itxn->security);
-        break;
-
-    case QIF_A_CGSHORT:
-    case QIF_A_CGSHORTX:
-    case QIF_A_REINVSG:
-    case QIF_A_REINVSH:
-        itxn->far_cat.acct = qif_default_cgshort_acct(ctx, itxn->security);
-        break;
-
-    case QIF_A_DIV:
-    case QIF_A_DIVX:
-    case QIF_A_REINVDIV:
-        itxn->far_cat.acct = qif_default_dividend_acct(ctx, itxn->security);
-        break;
-
-    case QIF_A_INTINC:
-    case QIF_A_INTINCX:
-    case QIF_A_REINVINT:
-        itxn->far_cat.acct = qif_default_interest_acct(ctx, itxn->security);
-        break;
-
-    case QIF_A_MARGINT:
-    case QIF_A_MARGINTX:
-        itxn->far_cat.acct = qif_default_margin_interest_acct(ctx);
-        break;
-
-    case QIF_A_RTRNCAP:
-    case QIF_A_RTRNCAPX:
-        itxn->far_cat.acct = qif_default_capital_return_acct(ctx, itxn->security);
-        break;
-
-    case QIF_A_SHRSIN:
-    case QIF_A_SHRSOUT:
-        itxn->far_cat.acct = qif_default_equity_holding(ctx, itxn->security);
-        break;
-
-    case QIF_A_STKSPLIT:
-        itxn->far_cat.acct = qif_default_stock_acct(ctx, itxn->security);
-        break;
-
-    default:
-        break;
-    }
-
-    /* If we don't have a far acct (or far category) then reset the flag */
-    if (!itxn->far_cat.obj)
-        itxn->far_cat_is_acct = FALSE;
-
-    /* And now fill in the "near" and "far" splits.  In particular we need
-     *
-     *	NEAR:	txn->from_acct, near_split->amount, value
-     *	FAR:	cat, far_split->amount, value
-     */
-    switch (itxn->action)
-    {
-    case QIF_A_BUY:
-    case QIF_A_BUYX:
-    case QIF_A_REINVDIV:
-    case QIF_A_REINVINT:
-    case QIF_A_REINVLG:
-    case QIF_A_REINVMD:
-    case QIF_A_REINVSG:
-    case QIF_A_REINVSH:
-    case QIF_A_SHRSIN:
-        near_split->amount = itxn->shares;
-        near_split->value = split_value;
-        far_split->amount = far_split->value = gnc_numeric_neg(itxn->amount);
-        break;
-
-    case QIF_A_SELL:
-    case QIF_A_SELLX:
-    case QIF_A_SHRSOUT:
-        near_split->amount = gnc_numeric_neg(itxn->shares);
-        near_split->value = gnc_numeric_neg(split_value);
-        far_split->amount = far_split->value = itxn->amount;
-        break;
-
-    case QIF_A_CGLONG:
-    case QIF_A_CGLONGX:
-    case QIF_A_CGMID:
-    case QIF_A_CGMIDX:
-    case QIF_A_CGSHORT:
-    case QIF_A_CGSHORTX:
-    case QIF_A_DIV:
-    case QIF_A_DIVX:
-    case QIF_A_INTINC:
-    case QIF_A_INTINCX:
-    case QIF_A_MISCINC:
-    case QIF_A_MISCINCX:
-    case QIF_A_RTRNCAP:
-    case QIF_A_RTRNCAPX:
-    case QIF_A_XIN:
-        near_split->amount = near_split->value = itxn->amount;
-        far_split->amount = far_split->value = gnc_numeric_neg(itxn->amount);
-        break;
-
-    case QIF_A_MARGINT:
-    case QIF_A_MARGINTX:
-    case QIF_A_MISCEXP:
-    case QIF_A_MISCEXPX:
-    case QIF_A_XOUT:
-        near_split->amount = near_split->value = gnc_numeric_neg(itxn->amount);
-        far_split->amount = far_split->value = itxn->amount;
-        break;
-
-    case QIF_A_STKSPLIT:
-        /* QIF just specifies the split ratio, not the number of shares
-         * in and out, so we have to fetch the number of shares from the
-         * security account..  FEH!
-         */
-
-        near_split->value = gnc_numeric_neg(split_value);
-        far_split->value = split_value;
-
-        /* XXX: FIXME: compute in-shares/out-shares based on ratio here:
-         *
-         * splitratio = num-shares / 10;
-         * in_shares = gnc_account_get_balance(near_acct);
-         * out_shares = in_shares * splitratio;
-         *
-         * near_split->amount = out_shares;
-         * far_split->amount = gnc_numeric_neg(in_shares);
-         *
-         * We know (later) that near_split == txn->default_split and
-         * far_split == txn->splits->data, so we'll just special-case this
-         * kind of txn when we convert to GNC later.
-         */
-
-        break;
-
-    default:
-        break;
-    }
-
-    /* Just make sure to set that it's an account, not a category */
-    far_split->cat.obj = itxn->far_cat.obj;
-    if (itxn->far_cat_is_acct)
-        far_split->cat_is_acct = TRUE;
-
-    /* make the commission split if we need it, then add it to the split-list  */
-    if (itxn->commissionstr)
-    {
-        comm_split = qif_split_new();
-        comm_split->cat.acct = qif_default_commission_acct(ctx);
-        comm_split->cat_is_acct = TRUE;
-        comm_split->amount = itxn->commission;
-        comm_split->value = itxn->commission;
-
-        txn->splits = g_list_prepend(txn->splits, comm_split);
-    }
-
-    /* Push the "far split" into the txn split-list */
-    txn->splits = g_list_prepend(txn->splits, far_split);
-
-    /* Free parsed strings.. */
-    g_free(cat);
-    g_free(cat_class);
-    g_free(miscx);
-    g_free(miscx_class);
-}
-
-
-/* Other handlers */
-static void
-qif_autoswitch_set(QifContext ctx)
-{
-    qif_set_flag(ctx->parse_flags, QIF_F_IGNORE_ACCOUNTS);
-}
-
-static void
-qif_autoswitch_clear(QifContext ctx)
-{
-    qif_clear_flag(ctx->parse_flags, QIF_F_IGNORE_ACCOUNTS);
-}
-
-/********************************************************************************
- * find or make ...
- */
-
-QifAccount
-find_or_make_acct(QifContext ctx, char *name, GList *types)
-{
-    QifAccount res;
-
-    res = (QifAccount)qif_object_map_lookup(ctx, QIF_O_ACCOUNT, name);
-    if (res)
-        g_free(name);
-    else
-    {
-        res = qif_account_new();
-        res->name = name;
-        res->type_list = types;
-
-        qif_object_map_insert(ctx, name, (QifObject)res);
-    }
-
-    return res;
-}
-
-QifCategory
-find_or_make_cat(QifContext ctx, char *name)
-{
-    QifCategory res;
-
-    res = (QifCategory)qif_object_map_lookup(ctx, QIF_O_CATEGORY, name);
-    if (res)
-        g_free(name);
-    else
-    {
-        res = qif_cat_new();
-
-        res->name = name;
-
-        qif_object_map_insert(ctx, name, (QifObject)res);
-    }
-
-    return res;
-}
-
-QifClass
-find_or_make_class(QifContext ctx, char *name)
-{
-    QifClass res;
-
-    res = (QifClass)qif_object_map_lookup(ctx, QIF_O_CLASS, name);
-    if (res)
-        g_free(name);
-    else
-    {
-        res = qif_class_new();
-        res->name = name;
-        qif_object_map_insert(ctx, name, (QifObject)res);
-    }
-    return res;
-}
-
-/*****************************************************************************/
-
-/*
- * initialize handlers
- */
-void
-qif_object_init(void)
-{
-    int i;
-    static struct
-    {
-        QifType		type;
-        struct _QifHandler	handler;
-    } handlers[] =
-    {
-        { QIF_TYPE_BANK, { qif_txn_init, qif_txn_parse, qif_txn_end_acct } },
-        { QIF_TYPE_CASH, { qif_txn_init, qif_txn_parse, qif_txn_end_acct } },
-        { QIF_TYPE_CCARD, { qif_txn_init, qif_txn_parse, qif_txn_end_acct } },
-        { QIF_TYPE_INVST, { qif_txn_init, qif_txn_invst_parse, qif_txn_end_acct } },
-        { QIF_TYPE_PORT, { qif_txn_init, qif_txn_invst_parse, qif_txn_end_acct } },
-        { QIF_TYPE_OTH_A, { qif_txn_init, qif_txn_parse, qif_txn_end_acct } },
-        { QIF_TYPE_OTH_L, { qif_txn_init, qif_txn_parse, qif_txn_end_acct } },
-        { QIF_TYPE_CLASS, { NULL, qif_class_parse, NULL } },
-        { QIF_TYPE_CAT, { NULL, qif_cat_parse, NULL } },
-        { QIF_TYPE_SECURITY, { NULL, qif_security_parse, NULL } },
-        { QIF_ACCOUNT, { NULL, qif_account_parse, NULL } },
-        { QIF_AUTOSWITCH, { qif_autoswitch_set, NULL, NULL } },
-        { QIF_CLEAR_AUTOSWITCH, { qif_autoswitch_clear, NULL, NULL } },
-        { 0, {NULL, NULL, NULL} }
-    };
-
-    for (i = 0; handlers[i].type > 0; i++)
-    {
-        if (handlers[i].type <= 0)
-        {
-            PERR("Invalid type?!?  (%d @ %d)", handlers[i].type, i);
-        }
-        else
-            qif_register_handler(handlers[i].type, &(handlers[i].handler));
-    }
-}
diff --git a/gnucash/import-export/qif/qif-objects.h b/gnucash/import-export/qif/qif-objects.h
deleted file mode 100644
index 2d28e59..0000000
--- a/gnucash/import-export/qif/qif-objects.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * qif-objects.h -- QIF objects for the QIF importer
- *
- * Written By:	Derek Atkins <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#ifndef QIF_OBJECTS_H
-#define QIF_OBJECTS_H
-
-typedef struct _QifObject *QifObject;
-typedef struct _QifData *QifData;
-
-struct _QifObject
-{
-    const char*	type;
-    void		(*destroy)(QifObject);
-
-    /* QIF Objects contain data beyond this point.. */
-};
-
-#define QIF_O_ACCOUNT	"qif-acct"
-typedef struct _QifAccount *QifAccount;
-
-#define QIF_O_CATEGORY	"qif-cat"
-typedef struct _QifCategory *QifCategory;
-
-#define QIF_O_CLASS	"qif-class"
-typedef struct _QifClass *QifClass;
-
-#define QIF_O_SECURITY	"qif-security"
-typedef struct _QifSecurity *QifSecurity;
-
-#define QIF_O_TXN	"qif-txn"
-typedef struct _QifTxn *QifTxn;
-typedef struct _QifSplit *QifSplit;
-typedef struct _QifInvstTxn *QifInvstTxn;
-
-void qif_object_init(void);
-
-QifAccount find_or_make_acct(QifContext ctx, char *name, GList *types);
-QifCategory find_or_make_cat(QifContext ctx, char *name);
-QifClass find_or_make_class(QifContext ctx, char *name);
-
-/* merge the object into the context.  Returns the object that's in
- * the context, which is either the supplied object or the
- * already-existing object.
- */
-QifAccount qif_account_merge(QifContext ctx, QifAccount acct);
-QifCategory qif_cat_merge(QifContext ctx, QifCategory cat);
-QifClass qif_class_merge(QifContext ctx, QifClass qclass);
-QifSecurity qif_security_merge(QifContext ctx, QifSecurity security);
-
-#endif /* QIF_OBJECTS_H */
diff --git a/gnucash/import-export/qif/qif-parse.c b/gnucash/import-export/qif/qif-parse.c
deleted file mode 100644
index f291c86..0000000
--- a/gnucash/import-export/qif/qif-parse.c
+++ /dev/null
@@ -1,935 +0,0 @@
-/*
- * qif-parse.c -- parse QIF
- *
- * Written by:        Derek Atkins <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <glib.h>
-#include <glib/gi18n.h>
-#include <string.h>
-
-/* For regex */
-#include <sys/types.h>
-#include <regex.h>
-
-#include <stdarg.h>
-
-#include "gnc-engine.h"
-#include "gnc-ui-util.h"
-
-#include "qif-import-p.h"
-#include "qif-objects-p.h"
-
-#include "import-parse.h"
-
-static QofLogModule log_module = GNC_MOD_IMPORT;
-
-/* An array of handlers for the various bang-types */
-static QifHandler qif_handlers[QIF_TYPE_MAX+1] = { NULL };
-
-/* Parser Regular Expressions */
-static gboolean qifp_regex_compiled = FALSE;
-static regex_t category_regex;
-
-/* A Hash Table of bang-types */
-static GHashTable *qif_bangtype_map = NULL;
-
-/* A Hash Table of action strings */
-static GHashTable *qif_action_map = NULL;
-
-/* A Hash Table of account types */
-static GHashTable *qif_atype_map = NULL;
-
-/************************************************************************/
-
-/* Register a handler */
-void
-qif_register_handler(QifType type, QifHandler handler)
-{
-    if (type <= 0 || type > QIF_TYPE_MAX)
-    {
-        PERR("Invalid type: %d", type);
-        return;
-    }
-    qif_handlers[type] = handler;
-}
-
-static void
-compile_regex()
-{
-    regcomp(&category_regex,
-            "^ *(\\[)?([^]/|]*)(]?)(/([^|]*))?(\\|(\\[)?([^]/]*)(]?)(/(.*))?)? *$",
-            REG_EXTENDED);
-
-    qifp_regex_compiled = TRUE;
-}
-
-#define QIF_ADD_TYPE(ts,t) \
-        g_hash_table_insert(qif_bangtype_map, ts, GINT_TO_POINTER(t)); \
-        g_hash_table_insert(qif_bangtype_map, _(ts), GINT_TO_POINTER(t));
-
-static void
-build_bangtype_map()
-{
-    g_return_if_fail(!qif_bangtype_map);
-
-    qif_bangtype_map = g_hash_table_new(g_str_hash, g_str_equal);
-    g_assert(qif_bangtype_map);
-
-    /* Translators FIXME: It is unclear whether these strings should
-       really be translated, and if yes, into which translation. */
-    QIF_ADD_TYPE(N_("type:bank"), QIF_TYPE_BANK);
-    QIF_ADD_TYPE(N_("type:cash"), QIF_TYPE_CASH);
-    QIF_ADD_TYPE(N_("type:ccard"), QIF_TYPE_CCARD);
-    QIF_ADD_TYPE(N_("type:invst"), QIF_TYPE_INVST);
-    QIF_ADD_TYPE(N_("type:port"), QIF_TYPE_PORT);
-    QIF_ADD_TYPE(N_("type:oth a"), QIF_TYPE_OTH_A);
-    QIF_ADD_TYPE(N_("type:oth l"), QIF_TYPE_OTH_L);
-    QIF_ADD_TYPE(N_("type:class"), QIF_TYPE_CLASS);
-    QIF_ADD_TYPE(N_("type:cat"), QIF_TYPE_CAT);
-    QIF_ADD_TYPE(N_("type:security"), QIF_TYPE_SECURITY);
-    QIF_ADD_TYPE(N_("account"), QIF_ACCOUNT);
-    QIF_ADD_TYPE(N_("option:autoswitch"), QIF_AUTOSWITCH);
-    QIF_ADD_TYPE(N_("clear:autoswitch"), QIF_CLEAR_AUTOSWITCH);
-}
-#undef QIF_ADD_TYPE
-
-#define QIF_ADD_ACT(ts,t) \
-        g_hash_table_insert(qif_action_map, ts, GINT_TO_POINTER(t));
-
-static void
-build_action_map()
-{
-    g_return_if_fail(!qif_action_map);
-
-    qif_action_map = g_hash_table_new(g_str_hash, g_str_equal);
-    g_assert(qif_action_map);
-
-    QIF_ADD_ACT("buy", QIF_A_BUY);
-    QIF_ADD_ACT("cvrshrt", QIF_A_BUY);
-    QIF_ADD_ACT("kauf", QIF_A_BUY);
-    QIF_ADD_ACT("buyx", QIF_A_BUYX);
-    QIF_ADD_ACT("cvrshrtx", QIF_A_BUYX);
-    QIF_ADD_ACT("kaufx", QIF_A_BUYX);
-    QIF_ADD_ACT("cglong", QIF_A_CGLONG);
-    QIF_ADD_ACT("kapgew", QIF_A_CGLONG); /* Kapitalgewinnsteuer */
-    QIF_ADD_ACT("cglongx", QIF_A_CGLONG);
-    QIF_ADD_ACT("kapgewx", QIF_A_CGLONG);
-    QIF_ADD_ACT("cgmid", QIF_A_CGMID);
-    QIF_ADD_ACT("cgmidx", QIF_A_CGMIDX);
-    QIF_ADD_ACT("cgshort", QIF_A_CGSHORT);
-    QIF_ADD_ACT("k.gewsp", QIF_A_CGSHORT);
-    QIF_ADD_ACT("cgshortx", QIF_A_CGSHORTX);
-    QIF_ADD_ACT("k.gewspx", QIF_A_CGSHORTX);
-    QIF_ADD_ACT("div", QIF_A_DIV); /* dividende */
-    QIF_ADD_ACT("divx", QIF_A_DIVX);
-    //QIF_ADD_ACT("exercise", QIF_A_EXERCISE);
-    //QIF_ADD_ACT("exercisex", QIF_A_EXERCISEX);
-    //QIF_ADD_ACT("expire", QIF_A_EXPIRE);
-    //QIF_ADD_ACT("grant", QIF_A_GRANT);
-    QIF_ADD_ACT("int", QIF_A_INTINC);
-    QIF_ADD_ACT("intinc", QIF_A_INTINC);
-    QIF_ADD_ACT("aktzu", QIF_A_INTINC); /* zinsen */
-    QIF_ADD_ACT("intx", QIF_A_INTINCX);
-    QIF_ADD_ACT("intincx", QIF_A_INTINCX);
-    QIF_ADD_ACT("margint", QIF_A_MARGINT);
-    QIF_ADD_ACT("margintx", QIF_A_MARGINTX);
-    QIF_ADD_ACT("miscexp", QIF_A_MISCEXP);
-    QIF_ADD_ACT("miscexpx", QIF_A_MISCEXPX);
-    QIF_ADD_ACT("miscinc", QIF_A_MISCINC);
-    QIF_ADD_ACT("cash", QIF_A_MISCINC);
-    QIF_ADD_ACT("miscincx", QIF_A_MISCINCX);
-    QIF_ADD_ACT("reinvdiv", QIF_A_REINVDIV);
-    QIF_ADD_ACT("reinvint", QIF_A_REINVINT);
-    QIF_ADD_ACT("reinvzin", QIF_A_REINVINT);
-    QIF_ADD_ACT("reinvlg", QIF_A_REINVLG);
-    QIF_ADD_ACT("reinvkur", QIF_A_REINVLG);
-    QIF_ADD_ACT("reinvmd", QIF_A_REINVMD);
-    QIF_ADD_ACT("reinvsg", QIF_A_REINVSG);
-    QIF_ADD_ACT("reinvksp", QIF_A_REINVSG);
-    QIF_ADD_ACT("reinvsh", QIF_A_REINVSH);
-    QIF_ADD_ACT("reminder", QIF_A_REMINDER);
-    QIF_ADD_ACT("erinnerg", QIF_A_REMINDER);
-    QIF_ADD_ACT("rtrncap", QIF_A_RTRNCAP);
-    QIF_ADD_ACT("rtrncapx", QIF_A_RTRNCAPX);
-    QIF_ADD_ACT("sell", QIF_A_SELL);
-    QIF_ADD_ACT("shtsell", QIF_A_SELL);
-    QIF_ADD_ACT("verkauf", QIF_A_SELL); /* verkaufen */
-    QIF_ADD_ACT("sellx", QIF_A_SELLX);
-    QIF_ADD_ACT("shtsellx", QIF_A_SELLX);
-    QIF_ADD_ACT("verkaufx", QIF_A_SELLX); /* verkaufen */
-    QIF_ADD_ACT("shrsin", QIF_A_SHRSIN);
-    QIF_ADD_ACT("aktzu", QIF_A_SHRSIN);
-    QIF_ADD_ACT("shrsout", QIF_A_SHRSOUT);
-    QIF_ADD_ACT("aktab", QIF_A_SHRSOUT);
-    QIF_ADD_ACT("stksplit", QIF_A_STKSPLIT);
-    QIF_ADD_ACT("aktsplit", QIF_A_STKSPLIT);
-    //QIF_ADD_ACT("vest", QIF_A_VEST);
-    QIF_ADD_ACT("xin", QIF_A_XIN);
-    QIF_ADD_ACT("contribx", QIF_A_XIN);
-    QIF_ADD_ACT("xout", QIF_A_XOUT);
-    QIF_ADD_ACT("withdrwx", QIF_A_XOUT);
-}
-#undef QIF_ADD_ACT
-
-static GList *
-make_list(int count, ...)
-{
-    GList *result = NULL;
-    GNCAccountType type;
-    va_list ap;
-
-    va_start (ap, count);
-    while (count--)
-    {
-        type = va_arg (ap, GNCAccountType);
-        result = g_list_prepend (result, GINT_TO_POINTER(type));
-    }
-    va_end (ap);
-
-
-    return g_list_reverse(result);
-}
-
-#define QIF_ADD_ATYPE(a,t) g_hash_table_insert(qif_atype_map, a, t);
-static void
-build_atype_map()
-{
-    g_return_if_fail(!qif_atype_map);
-
-    qif_atype_map = g_hash_table_new(g_str_hash, g_str_equal);
-    g_assert(qif_atype_map);
-
-    QIF_ADD_ATYPE("bank", make_list(1, ACCT_TYPE_BANK));
-    QIF_ADD_ATYPE("port", make_list(1, ACCT_TYPE_BANK));
-    QIF_ADD_ATYPE("cash", make_list(1, ACCT_TYPE_CASH));
-    QIF_ADD_ATYPE("ccard", make_list(1, ACCT_TYPE_CREDIT));
-    QIF_ADD_ATYPE("invst", make_list(3, ACCT_TYPE_BANK, ACCT_TYPE_STOCK,
-                                     ACCT_TYPE_MUTUAL));
-    QIF_ADD_ATYPE("oth a", make_list(3, ACCT_TYPE_ASSET, ACCT_TYPE_BANK,
-                                     ACCT_TYPE_CASH));
-    QIF_ADD_ATYPE("oth l", make_list(2, ACCT_TYPE_LIABILITY, ACCT_TYPE_CREDIT));
-    QIF_ADD_ATYPE("mutual", make_list(3, ACCT_TYPE_BANK, ACCT_TYPE_MUTUAL,
-                                      ACCT_TYPE_STOCK));
-
-    /* Internal types */
-    QIF_ADD_ATYPE("__any_bank__", make_list(5, ACCT_TYPE_BANK, ACCT_TYPE_CREDIT,
-                                            ACCT_TYPE_CASH, ACCT_TYPE_ASSET,
-                                            ACCT_TYPE_LIABILITY));
-    QIF_ADD_ATYPE("__all__", make_list(7, ACCT_TYPE_BANK, ACCT_TYPE_CREDIT,
-                                       ACCT_TYPE_CASH, ACCT_TYPE_ASSET,
-                                       ACCT_TYPE_LIABILITY, ACCT_TYPE_STOCK,
-                                       ACCT_TYPE_MUTUAL));
-    QIF_ADD_ATYPE("__stock__", make_list(2, ACCT_TYPE_STOCK, ACCT_TYPE_MUTUAL));
-    QIF_ADD_ATYPE("__income__", make_list(1, ACCT_TYPE_INCOME));
-    QIF_ADD_ATYPE("__expense__", make_list(1, ACCT_TYPE_EXPENSE));
-    QIF_ADD_ATYPE("__equity__", make_list(1, ACCT_TYPE_EQUITY));
-}
-#undef QIF_ADD_ATYPE
-
-/************************************************************************/
-
-/*
- * We've got a !Type line.  Parse the line into the appropriate
- * type and then initialize the handler.
- */
-void
-qif_parse_bangtype(QifContext ctx, const char *line)
-{
-    QifType type;
-    char *bangtype;
-    gpointer result;
-
-    g_return_if_fail(line && *line == '!');
-
-    if (!qif_bangtype_map)
-        build_bangtype_map();
-
-    /* Make a local copy so we can manipulate it.
-     * - strip off leading/trailing whitespace
-     * - make it all lower case
-     */
-    bangtype = g_utf8_strdown(line + 1, -1);
-    g_strstrip(bangtype);
-
-    /* In some cases we get "!Type Bank" -- change the space to a colon */
-    if (!strncmp(bangtype, "type ", 5))
-        bangtype[5] = ':';
-
-    /* Lookup the bangtype in the map and then destroy the local copy */
-    result = g_hash_table_lookup(qif_bangtype_map, bangtype);
-    g_free(bangtype);
-
-    if (!result)
-    {
-        PWARN("Unknown bang-type at line %d: %s.  Ignored", ctx->lineno, line);
-        return;
-    }
-    type = GPOINTER_TO_INT(result);
-
-    /* Set the current context parse type and handler */
-    ctx->parse_type = type;
-    ctx->handler = qif_handlers[type];
-
-    /* now initialize this new parse type (if there's an init function) */
-    if (ctx->handler && ctx->handler->init)
-        ctx->handler->init(ctx);
-}
-
-/* returns TRUE if successful, FALSE if there is a problem */
-gboolean
-qif_parse_split_category(const char* str,
-                         char** cat, gboolean *cat_is_acct, char** cat_class,
-                         char** miscx_cat, gboolean *miscx_cat_is_acct,
-                         char **miscx_class)
-{
-    /* This is a pretty f**ked up string.  Basically it looks like:
-     *  ([)cat-or-acct(])(/(class))(|([)cat-of-acct(])(/ext))
-     *
-     * where data in parens is "optional" (depending on the context).
-     *
-     * examples from reality:
-     *
-     * category
-     * category:subcategory
-     * category/class
-     * category:subcat/class
-     * [account]
-     * [account]/class
-     *
-     * cat/cat-class|miscx-cat/miscx-class
-     */
-
-    regmatch_t pmatch[12];
-
-    g_return_val_if_fail(cat && cat_is_acct && cat_class &&
-                         miscx_cat && miscx_cat_is_acct && miscx_class, FALSE);
-
-
-    if (!qifp_regex_compiled)
-        compile_regex();
-
-    if (regexec(&category_regex, str, 12, pmatch, 0) != 0)
-    {
-        PERR("category match failed");
-        return FALSE;
-    }
-
-    /*
-     * what the substrings mean:
-     * 1 the opening [ for a transfer
-     * 2 the category
-     * 3 the closing ]
-     * 4 the class /
-     * 5 the class
-     * 6 the miscx expression (whole thing)
-     * 7 the opening [
-     * 8 the miscx category
-     * 9 the closing ]
-     * 10 the class /
-     * 11 the class
-     */
-
-    if (pmatch[2].rm_so == -1)
-    {
-        PERR("no category match found!");
-        return FALSE;
-    }
-
-    /* catgory name */
-    *cat = g_strndup(str + pmatch[2].rm_so, pmatch[2].rm_eo - pmatch[2].rm_so);
-    /* category is account? */
-    *cat_is_acct = (pmatch[1].rm_so != -1 && pmatch[3].rm_so != -1);
-    /* category class */
-    *cat_class = (pmatch[4].rm_so != -1 ?
-                  g_strndup(str + pmatch[5].rm_so, pmatch[5].rm_eo - pmatch[5].rm_so) :
-                  NULL);
-
-    /* miscx category name */
-    *miscx_cat = (pmatch[6].rm_so != -1 ?
-                  g_strndup(str + pmatch[8].rm_so, pmatch[8].rm_eo - pmatch[8].rm_so) :
-                  NULL);
-    /* miscx cat is acct */
-    *miscx_cat_is_acct  = (pmatch[7].rm_so != -1 && pmatch[9].rm_so != -1);
-    /* miscx class */
-    *miscx_class = (pmatch[10].rm_so != -1 ?
-                    g_strndup(str + pmatch[11].rm_so,
-                              pmatch[11].rm_eo - pmatch[11].rm_so) : NULL);
-
-    return TRUE;
-}
-
-/*
- * qif_parse_cleared -- parse the 'C'leared field of a QIF Transaction.
- * returns the QIF reconciled flag.
- *
- * * means cleared, x or X means reconciled, and ! or ? mean some
- * budget related stuff I don't understand.
- */
-QifRecnFlag
-qif_parse_cleared(QifLine line)
-{
-    g_return_val_if_fail(line, QIF_R_NO);
-    g_return_val_if_fail(line->line, QIF_R_NO);
-
-    switch (*line->line)
-    {
-    case '*':
-        return QIF_R_CLEARED;
-    case 'x':
-    case 'X':
-        return QIF_R_RECONCILED;
-    case '?':
-    case '!':
-        return QIF_R_BUDGETED;
-    default:
-        PERR("Unknown QIF Cleared flag at line %d: %s", line->lineno, line->line);
-        return QIF_R_NO;
-    }
-}
-
-QifAction qif_parse_action(QifLine line)
-{
-    QifAction qaction;
-    gpointer result;
-    char *action;
-
-    g_return_val_if_fail(line, QIF_A_NONE);
-    g_return_val_if_fail(line->line, QIF_A_NONE);
-
-    if (!qif_action_map)
-        build_action_map();
-
-    /* Duplicate the action and force it to lower case and strip any spaces */
-    action = g_utf8_strdown(line->line, -1);
-    g_strstrip(action);
-
-    result = g_hash_table_lookup(qif_action_map, action);
-    g_free(action);
-
-    if (!result)
-    {
-        /* XXX: pop up a dialog? */
-        PWARN("Unknown Action at line %d: %s.  Some transactions may be discarded",
-              line->lineno, line->line);
-        return QIF_A_NONE;
-    }
-
-    qaction = GPOINTER_TO_INT(result);
-    return qaction;
-}
-
-GList * qif_parse_acct_type(const char *str, gint lineno)
-{
-    GList *result;
-    char *type;
-
-    if (!qif_atype_map)
-        build_atype_map();
-
-    /* Duplicate the type and force it to lower case and strip any spaces */
-    type = g_utf8_strdown(str, -1);
-    g_strstrip(type);
-
-    result = g_hash_table_lookup(qif_atype_map, type);
-    g_free(type);
-
-    if (!result)
-    {
-        PWARN("Unknown account type at line %d: %s. ", lineno, str);
-        result = g_hash_table_lookup(qif_atype_map, "bank");
-        g_return_val_if_fail(result, NULL);
-    }
-
-    return result;
-}
-
-GList * qif_parse_acct_type_guess(QifType type)
-{
-    const char *atype = NULL;
-
-    switch (type)
-    {
-    case QIF_TYPE_BANK:
-        atype = "bank";
-        break;
-    case QIF_TYPE_CASH:
-        atype = "cash";
-        break;
-    case QIF_TYPE_CCARD:
-        atype = "ccard";
-        break;
-    case QIF_TYPE_INVST:
-        atype = "invst";
-        break;
-    case QIF_TYPE_PORT:
-        atype = "port";
-        break;
-    case QIF_TYPE_OTH_A:
-        atype = "oth a";
-        break;
-    case QIF_TYPE_OTH_L:
-        atype = "oth l";
-        break;
-    default:
-        return NULL;
-    }
-
-    return qif_parse_acct_type(atype, -1);
-}
-
-/***********************************************************************
- * Parsing numbers and dates...
- */
-
-typedef struct _parse_helper
-{
-    QifContext                ctx;
-
-    GncImportFormat        budget;
-    GncImportFormat        limit;
-    GncImportFormat        amount;
-    GncImportFormat        d_amount;
-    GncImportFormat        price;
-    GncImportFormat        shares;
-    GncImportFormat        commission;
-    GncImportFormat        date;
-} *parse_helper_t;
-
-#define QIF_PARSE_CHECK_NUMBER(str,help) { \
-        if (str) (help) = gnc_import_test_numeric((str), (help)); \
-}
-#define QIF_PARSE_PARSE_NUMBER(str,fmt,val) { \
-        if (str) gnc_import_parse_numeric((str), (fmt), (val)); \
-}
-
-static void
-qif_parse_check_account(gpointer key, gpointer val, gpointer data)
-{
-    parse_helper_t helper = data;
-    QifAccount acct = val;
-
-    QIF_PARSE_CHECK_NUMBER(acct->limitstr, helper->limit);
-    QIF_PARSE_CHECK_NUMBER(acct->budgetstr, helper->budget);
-}
-
-static void
-qif_parse_parse_account(gpointer key, gpointer val, gpointer data)
-{
-    parse_helper_t helper = data;
-    QifAccount acct = val;
-
-    QIF_PARSE_PARSE_NUMBER(acct->limitstr, helper->limit, &acct->limit);
-    QIF_PARSE_PARSE_NUMBER(acct->budgetstr, helper->budget, &acct->budget);
-}
-
-static void
-qif_parse_check_category(gpointer key, gpointer val, gpointer data)
-{
-    parse_helper_t helper = data;
-    QifCategory cat = val;
-
-    QIF_PARSE_CHECK_NUMBER(cat->budgetstr, helper->budget);
-}
-
-static void
-qif_parse_parse_category(gpointer key, gpointer val, gpointer data)
-{
-    parse_helper_t helper = data;
-    QifCategory cat = val;
-
-    QIF_PARSE_PARSE_NUMBER(cat->budgetstr, helper->budget, &cat->budget);
-}
-
-static void
-qif_parse_check_txn(gpointer val, gpointer data)
-{
-    parse_helper_t helper = data;
-    QifTxn txn = val;
-    QifSplit split;
-    QifInvstTxn itxn;
-    GList *node;
-
-    /* Check the date */
-    helper->date = gnc_import_test_date(txn->datestr, helper->date);
-
-    /* If this is an investment transaction, then all the info is in
-     * the invst_info.  Otherwise it's all in the splits.
-     */
-    itxn = txn->invst_info;
-    if (itxn)
-    {
-        QIF_PARSE_CHECK_NUMBER(itxn->amountstr, helper->amount);
-        QIF_PARSE_CHECK_NUMBER(itxn->d_amountstr, helper->d_amount);
-        QIF_PARSE_CHECK_NUMBER(itxn->pricestr, helper->price);
-        QIF_PARSE_CHECK_NUMBER(itxn->sharesstr, helper->shares);
-        QIF_PARSE_CHECK_NUMBER(itxn->commissionstr, helper->commission);
-
-    }
-    else
-    {
-        split = txn->default_split;
-        node = txn->splits;
-        do
-        {
-            QIF_PARSE_CHECK_NUMBER(split->amountstr, helper->amount);
-
-            if (node)
-            {
-                split = node->data;
-                node = node->next;
-            }
-            else
-                split = NULL;
-        }
-        while (split);
-    }
-}
-
-static void
-qif_parse_parse_txn(gpointer val, gpointer data)
-{
-    parse_helper_t helper = data;
-    QifTxn txn = val;
-    QifSplit split;
-    QifInvstTxn itxn;
-    GList *node;
-
-    /* Parse the date */
-    gnc_import_parse_date(txn->datestr, helper->date, &txn->date);
-
-    /* If this is an investment transaction, then all the info is in
-     * the invst_info.  Otherwise it's all in the splits.
-     */
-    itxn = txn->invst_info;
-    if (itxn)
-    {
-        QIF_PARSE_PARSE_NUMBER(itxn->amountstr, helper->amount, &itxn->amount);
-        QIF_PARSE_PARSE_NUMBER(itxn->d_amountstr, helper->d_amount, &itxn->d_amount);
-        QIF_PARSE_PARSE_NUMBER(itxn->pricestr, helper->price, &itxn->price);
-        QIF_PARSE_PARSE_NUMBER(itxn->sharesstr, helper->shares, &itxn->shares);
-        QIF_PARSE_PARSE_NUMBER(itxn->commissionstr, helper->commission,
-                               &itxn->commission);
-
-        qif_invst_txn_setup_splits(helper->ctx, txn);
-
-    }
-    else
-    {
-        split = txn->default_split;
-        node = txn->splits;
-        do
-        {
-            QIF_PARSE_PARSE_NUMBER(split->amountstr, helper->amount, &split->amount);
-
-            if (node)
-            {
-                split = node->data;
-                node = node->next;
-            }
-            else
-                split = NULL;
-        }
-        while (split);
-
-        qif_txn_setup_splits(txn);
-    }
-}
-
-void
-qif_parse_all(QifContext ctx, gpointer arg)
-{
-    struct _parse_helper helper;
-
-    helper.ctx = ctx;
-
-    /* PARSE ACCOUNTS */
-
-    /* First, figure out the formats */
-    helper.limit = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
-    helper.budget = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
-    qif_object_map_foreach(ctx, QIF_O_ACCOUNT, qif_parse_check_account, &helper);
-
-    /* Make sure it's not ambiguous */
-    if (helper.limit & (helper.limit - 1)) helper.limit = GNCIF_NUM_PERIOD;
-    if (helper.budget & (helper.budget - 1)) helper.budget = GNCIF_NUM_PERIOD;
-
-    /* Now convert the numbers */
-    qif_object_map_foreach(ctx, QIF_O_ACCOUNT, qif_parse_parse_account, &helper);
-
-    /* PARSE CATEGORIES */
-
-    helper.budget = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
-    qif_object_map_foreach(ctx, QIF_O_CATEGORY, qif_parse_check_category, &helper);
-
-    /* make sure it's not ambiguous */
-    if (helper.budget & (helper.budget - 1)) helper.budget = GNCIF_NUM_PERIOD;
-
-    /* Now convert the numbers */
-    qif_object_map_foreach(ctx, QIF_O_CATEGORY, qif_parse_parse_category, &helper);
-
-    /* PARSE TRANSACTIONS */
-    helper.amount = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
-    helper.d_amount = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
-    helper.price = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
-    helper.shares = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
-    helper.commission = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
-    helper.date = GNCIF_DATE_MDY | GNCIF_DATE_DMY | GNCIF_DATE_YMD | GNCIF_DATE_YDM;
-
-    qif_object_list_foreach(ctx, QIF_O_TXN, qif_parse_check_txn, &helper);
-
-    /* check/fix ambiguities */
-    if (helper.amount & (helper.amount - 1)) helper.amount = GNCIF_NUM_PERIOD;
-    if (helper.d_amount & (helper.d_amount - 1)) helper.d_amount = GNCIF_NUM_PERIOD;
-    if (helper.price & (helper.price - 1)) helper.price = GNCIF_NUM_PERIOD;
-    if (helper.shares & (helper.shares - 1)) helper.shares = GNCIF_NUM_PERIOD;
-    if (helper.commission & (helper.commission - 1))
-        helper.commission = GNCIF_NUM_PERIOD;
-
-    if (helper.date & (helper.date - 1))
-    {
-        helper.date = gnc_import_choose_fmt(_("The Date format is ambiguous.  "
-                                              "Please choose the correct format."),
-                                            helper.date, arg);
-    }
-
-    /* now parse it.. */
-    qif_object_list_foreach(ctx, QIF_O_TXN, qif_parse_parse_txn, &helper);
-}
-
-typedef struct
-{
-    QifContext        ctx;
-    GList *        list;
-    const char*        type;
-} qif_merge_t;
-
-static void
-qif_merge_accts(gpointer key, gpointer value, gpointer data)
-{
-    qif_merge_t *merge = data;
-    QifAccount acct = value;
-
-    /* Merge into the context.  Remember items moved into the parent */
-    if (qif_account_merge(merge->ctx, acct) == acct)
-        merge->list = g_list_prepend(merge->list, acct->name);
-}
-
-static void
-qif_merge_cats(gpointer key, gpointer value, gpointer data)
-{
-    qif_merge_t *merge = data;
-    QifCategory cat = value;
-
-    /* Merge into the context.  Remember items moved into the parent */
-    if (qif_cat_merge(merge->ctx, cat) == cat)
-        merge->list = g_list_prepend(merge->list, cat->name);
-}
-
-static void
-qif_merge_classes(gpointer key, gpointer value, gpointer data)
-{
-    qif_merge_t *merge = data;
-    QifClass qclass = value;
-
-    /* Merge into the context.  Remember items moved into the parent */
-    if (qif_class_merge(merge->ctx, qclass) == qclass)
-        merge->list = g_list_prepend(merge->list, qclass->name);
-}
-
-static void
-qif_merge_secs(gpointer key, gpointer value, gpointer data)
-{
-    qif_merge_t *merge = data;
-    QifSecurity sec = value;
-
-    /* Merge into the context.  Remember items moved into the parent */
-    if (qif_security_merge(merge->ctx, sec) == sec)
-        merge->list = g_list_prepend(merge->list, sec->name);
-}
-
-static void
-qif_merge_del(gpointer obj, gpointer data)
-{
-    qif_merge_t *merge = data;
-    const char *name = obj;
-
-    qif_object_map_remove(merge->ctx, merge->type, name);
-}
-
-static void
-qif_massage_split(QifSplit split, QifContext ctx)
-{
-    const char *type = QIF_O_CATEGORY;
-    char *name;
-
-    if (split->cat.obj)
-    {
-        if (split->cat_is_acct)
-        {
-            type = QIF_O_ACCOUNT;
-            name = split->cat.acct->name;
-        }
-        else
-            name = split->cat.cat->name;
-
-        split->cat.obj = qif_object_map_lookup(ctx, type, name);
-    }
-
-    if (split->cat_class)
-    {
-        split->cat_class = (QifClass) qif_object_map_lookup(ctx, QIF_O_CLASS,
-                           split->cat_class->name);
-    }
-}
-
-static void
-qif_massage_itxn(QifInvstTxn itxn, QifContext ctx)
-{
-    const char *type = QIF_O_CATEGORY;
-    char *name;
-
-    if (itxn->far_cat.obj)
-    {
-        if (itxn->far_cat_is_acct)
-        {
-            type = QIF_O_ACCOUNT;
-            name = itxn->far_cat.acct->name;
-        }
-        else
-            name = itxn->far_cat.cat->name;
-
-        itxn->far_cat.obj = qif_object_map_lookup(ctx, type, name);
-    }
-}
-
-static void
-qif_massage_txn(gpointer obj, gpointer data)
-{
-    QifTxn txn = obj;
-    QifContext ctx = data;
-    QifSplit split;
-    GList *node;
-
-    if (txn->from_acct)
-        txn->from_acct = (QifAccount) qif_object_map_lookup(ctx, QIF_O_ACCOUNT,
-                         txn->from_acct->name);
-
-    if (txn->invst_info)
-        qif_massage_itxn(txn->invst_info, ctx);
-
-    if (txn->default_split)
-        qif_massage_split(txn->default_split, ctx);
-
-    for (node = txn->splits; node; node = node->next)
-    {
-        split = node->data;
-        qif_massage_split(split, ctx);
-    }
-}
-
-void
-qif_parse_merge_files(QifContext ctx)
-{
-    GList *node;
-    GList *accts = NULL;
-    GList *cats = NULL;
-    GList *classes = NULL;
-    GList *securities = NULL;
-    QifContext fctx;
-
-    qif_merge_t merge;
-
-    g_return_if_fail(ctx);
-
-    /* Make sure each of the "file" contexts have been parsed.
-     * note that we don't care about OUR context -- we can run this
-     * process multiple times safely.
-     */
-    for (node = ctx->files; node; node = node->next)
-    {
-        fctx = node->data;
-        g_return_if_fail(fctx->parsed);
-    }
-
-
-    /* Iterate over each file.  Merge the Accounts, Categories, Classes,
-     * Securities, and Transactions into the top-level context.  Be sure
-     * to re-point all Transaction/Split category/class/account pointers
-     * to the new top-level item.  Then be sure to remove the
-     * "duplicated" items so we don't double-free (as we don't refcount,
-     * either).
-     */
-    for (node = ctx->files; node; node = node->next)
-    {
-        fctx = node->data;
-
-        /* Merge accts, categories, classes, and securities */
-
-        merge.ctx = ctx;
-        merge.list = NULL;
-        qif_object_map_foreach(fctx, QIF_O_ACCOUNT, qif_merge_accts, &merge);
-        accts = merge.list;
-
-        merge.list = NULL;
-        qif_object_map_foreach(fctx, QIF_O_CATEGORY, qif_merge_cats, &merge);
-        cats = merge.list;
-
-        merge.list = NULL;
-        qif_object_map_foreach(fctx, QIF_O_CLASS, qif_merge_classes, &merge);
-        classes = merge.list;
-
-        merge.list = NULL;
-        qif_object_map_foreach(fctx, QIF_O_SECURITY, qif_merge_secs, &merge);
-        securities = merge.list;
-
-
-        /* repoint the transactions to the merged context data */
-        qif_object_list_foreach(fctx, QIF_O_TXN, qif_massage_txn, ctx);
-
-
-        /* then remove from the file context objects referenced in the top context */
-        merge.ctx = fctx;
-        merge.type = QIF_O_ACCOUNT;
-        g_list_foreach(accts, qif_merge_del, &merge);
-        g_list_free(accts);
-
-        merge.type = QIF_O_CATEGORY;
-        g_list_foreach(cats, qif_merge_del, &merge);
-        g_list_free(cats);
-
-        merge.type = QIF_O_CLASS;
-        g_list_foreach(classes, qif_merge_del, &merge);
-        g_list_free(classes);
-
-        merge.type = QIF_O_SECURITY;
-        g_list_foreach(securities, qif_merge_del, &merge);
-        g_list_free(securities);
-
-    }
-
-    /* We've been parsed */
-    ctx->parsed = TRUE;
-}
diff --git a/gnucash/import-export/qif/qif-parse.h b/gnucash/import-export/qif/qif-parse.h
deleted file mode 100644
index 11d439c..0000000
--- a/gnucash/import-export/qif/qif-parse.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * qif-parse.h -- routines for parsing pieces of a QIF file
- *
- * Written By:	Derek Atkins  <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#ifndef QIF_PARSE_H
-#define QIF_PARSE_H
-
-#include "qif-import.h"
-
-void qif_register_handler(QifType type, QifHandler handler);
-void qif_parse_bangtype(QifContext ctx, const char *line);
-
-gboolean
-qif_parse_split_category(const char* str,
-                         char** cat, gboolean *cat_is_acct, char** cat_class,
-                         char** miscx_cat, gboolean *miscx_cat_is_acct,
-                         char **miscx_class);
-
-gboolean qif_parse_numeric(QifLine line, gnc_numeric *num);
-QifRecnFlag qif_parse_cleared(QifLine line);
-QifAction qif_parse_action(QifLine line);
-
-/* The caller should never destroy this list */
-GList * qif_parse_acct_type(const char *str, gint lineno);
-GList * qif_parse_acct_type_guess(QifType type);
-
-/* Parse all objects */
-void qif_parse_all(QifContext ctx, gpointer ui_args);
-
-#endif /* QIF_PARSE_H */
diff --git a/gnucash/import-export/qif/test/CMakeLists.txt b/gnucash/import-export/qif/test/CMakeLists.txt
deleted file mode 100644
index d874ca3..0000000
--- a/gnucash/import-export/qif/test/CMakeLists.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-
-set(QIF_TEST_INCLUDE_DIRS
-  ${CMAKE_BINARY_DIR}/common
-  ${CMAKE_SOURCE_DIR}/gnucash/import-export/qif
-  ${CMAKE_SOURCE_DIR}/libgnucash/engine
-  ${CMAKE_SOURCE_DIR}/common/test-core
-  ${GLIB2_INCLUDE_DIRS}
-)
-set(QIF_TEST_LIBS gncmod-qif test-core)
-
-if (FALSE)
-  # Tests for this directory are not run.
-  gnc_add_test(test-link-qif test-link.c QIF_TEST_INCLUDE_DIRS QIF_TEST_LIBS)
-  gnc_add_test(test-qif test-qif.c QIF_TEST_INCLUDE_DIRS QIF_TEST_LIBS
-    GNC_TEST_FILES=${CMAKE_CURRENT_SOURCE_DIR}/test-files)
-endif()
-
-set_dist_list(test_qif_DIST CMakeLists.txt test-link.c test-qif.c test-files/test-1-bank-txn.qif)
diff --git a/gnucash/import-export/qif/test/test-files/test-1-bank-txn.qif b/gnucash/import-export/qif/test/test-files/test-1-bank-txn.qif
deleted file mode 100644
index 59592d0..0000000
--- a/gnucash/import-export/qif/test/test-files/test-1-bank-txn.qif
+++ /dev/null
@@ -1,6 +0,0 @@
-!Type:Bank
-D2003/01/27
-T123.45
-PTest Payee
-LTest Category
-^
diff --git a/gnucash/import-export/qif/test/test-link.c b/gnucash/import-export/qif/test/test-link.c
deleted file mode 100644
index fd55d42..0000000
--- a/gnucash/import-export/qif/test/test-link.c
+++ /dev/null
@@ -1,28 +0,0 @@
-/********************************************************************\
- * This program is free software; you can redistribute it and/or    *
- * modify it under the terms of the GNU General Public License as   *
- * published by the Free Software Foundation; either version 2 of   *
- * the License, or (at your option) any later version.              *
- *                                                                  *
- * This program is distributed in the hope that it will be useful,  *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
- * GNU General Public License for more details.                     *
- *                                                                  *
- * You should have received a copy of the GNU General Public License*
- * along with this program; if not, contact:                        *
- *                                                                  *
- * Free Software Foundation           Voice:  +1-617-542-5942       *
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
- * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
- *                                                                  *
-\********************************************************************/
-
-#include "qif-import.h"
-
-int
-main(int argc, char *argv[])
-{
-    qif_context_new();
-    return 0;
-}
diff --git a/gnucash/import-export/qif/test/test-qif.c b/gnucash/import-export/qif/test/test-qif.c
deleted file mode 100644
index c331bef..0000000
--- a/gnucash/import-export/qif/test/test-qif.c
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * test-qif.c -- Test the QIF Import routines.
- *
- * Created by:	Derek Atkins <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#include <glib.h>
-#include <libguile.h>
-
-#include "gnc-module.h"
-#include "qif-import.h"
-#include "qif-import-p.h"	/* Let's test some internal stuff, too */
-
-#include "test-stuff.h"
-
-/* XXX */
-extern void qif_object_init(void);
-
-static QifContext
-test_qif_load_file(QifContext ctx, const char *filename,
-                   gint txn_count, gint acct_count, gboolean needs_acct)
-{
-    QifContext file;
-
-    printf("qif loading \"%s\"...\n", filename);
-    file = qif_file_new(ctx, filename);
-    do_test(file != NULL, "failed to read file");
-    if (!file) return NULL;
-
-    do_test(qif_object_list_count(file, QIF_O_TXN) == txn_count,
-            "Transaction count didn't match");
-    do_test(qif_object_map_count(file, QIF_O_ACCOUNT) == acct_count,
-            "Account count didn't match");
-    do_test(qif_file_needs_account(file) == needs_acct,
-            "Needs account flad didn't match");
-
-    return file;
-}
-
-static void
-test_qif(void)
-{
-    QifContext ctx, file;
-    char *filename;
-    const char *location = g_getenv("GNC_TEST_FILES");
-    int i;
-
-    ctx = qif_context_new();
-    do_test(ctx != NULL, "failed to create the qif context");
-    if (!ctx) return;
-
-    if (!location)
-        location = "test-files";
-
-    for (i = 0; i < 1; i++)
-    {
-        filename = g_strdup_printf("%s/%s", location, "test-1-bank-txn.qif");
-        file = test_qif_load_file(ctx, filename, 1, 0, TRUE);
-        g_free(filename);
-        if (!file) continue;
-
-        if (qif_file_needs_account(file))
-            qif_file_set_default_account(file, "test-1-bank-txn");
-
-        do_test(qif_file_needs_account(file) == FALSE,
-                "'Needs account' flag not cleared properly");
-
-        do_test(qif_file_parse(file, NULL) == QIF_E_OK,
-                "file failed to parse.");
-    }
-
-    qif_context_destroy(ctx);
-
-    success("QIF test successful");
-}
-
-static void
-main_helper(void *closure, int argc, char **argv)
-{
-    qif_object_init();		/* XXX:FIXME */
-    test_qif();
-    print_test_results();
-    exit(get_rv());
-}
-
-int
-main(int argc, char **argv)
-{
-    scm_boot_guile(argc, argv, main_helper, NULL);
-    return 0;
-}
-
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 426b3f2..b8cc820 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -347,9 +347,6 @@ gnucash/import-export/ofx/gnc-ofx-import.c
 gnucash/import-export/ofx/gnc-ofx-kvp.c
 gnucash/import-export/ofx/gnc-plugin-ofx.c
 gnucash/import-export/ofx/gschemas/org.gnucash.dialogs.import.ofx.gschema.xml.in
-gnucash/import-export/qif/qif-context.c
-gnucash/import-export/qif/qif-file.c
-gnucash/import-export/qif/qif-objects.c
 gnucash/import-export/qif-imp/assistant-qif-import.c
 gnucash/import-export/qif-imp/dialog-account-picker.c
 gnucash/import-export/qif-imp/gncmod-qif-import.c
@@ -486,8 +483,7 @@ gnucash/report/standard-reports/general-journal.scm
 gnucash/report/standard-reports/general-ledger.scm
 gnucash/report/standard-reports/income-gst-statement.scm
 gnucash/report/standard-reports/income-statement.scm
-gnucash/report/standard-reports/net-barchart.scm
-gnucash/report/standard-reports/net-linechart.scm
+gnucash/report/standard-reports/net-charts.scm
 gnucash/report/standard-reports/portfolio.scm
 gnucash/report/standard-reports/price-scatter.scm
 gnucash/report/standard-reports/register.scm
@@ -575,7 +571,6 @@ libgnucash/backend/sql/gnc-sql-result.cpp
 libgnucash/backend/sql/gnc-tax-table-sql.cpp
 libgnucash/backend/sql/gnc-transaction-sql.cpp
 libgnucash/backend/sql/gnc-vendor-sql.cpp
-libgnucash/backend/xml/.#gnc-invoice-xml-v2.cpp
 libgnucash/backend/xml/gnc-account-xml-v2.cpp
 libgnucash/backend/xml/gnc-address-xml-v2.cpp
 libgnucash/backend/xml/gnc-backend-xml.cpp

commit 554001604ad653926c53dff8e9f7170bd865c84e
Merge: 0efe32e 754c047
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Jun 14 10:52:42 2018 -0700

    Merge Chris Lam's 'maint-chartjs-part-1' into maint.


commit 0efe32ea73197162a1bb5ee75338ee3d7ecdac1d
Merge: 8cae602 2e4e18e
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Jun 14 09:52:19 2018 -0700

    Merge Chris Lam's 'maint-test-net-charts' into maint.


commit 754c047892dd387b07ea35b2c3356f886e7b27dc
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Thu Jan 4 20:25:31 2018 +1100

    [net-charts] styling table for all charts

diff --git a/gnucash/report/standard-reports/net-charts.scm b/gnucash/report/standard-reports/net-charts.scm
index 6758826..9e1e885 100644
--- a/gnucash/report/standard-reports/net-charts.scm
+++ b/gnucash/report/standard-reports/net-charts.scm
@@ -499,12 +499,11 @@
              (gnc:html-document-add-object! document chart)
              (if show-table?
                  (let ((table (gnc:make-html-table)))
-                   (if linechart?
-                       (gnc:html-table-set-style!
-                        table "table"
-                        'attribute (list "border" 0)
-                        'attribute (list "cellspacing" 0)
-                        'attribute (list "cellpadding" 4)))
+                   (gnc:html-table-set-style!
+                    table "table"
+                    'attribute (list "border" 0)
+                    'attribute (list "cellspacing" 0)
+                    'attribute (list "cellpadding" 4))
                    (gnc:html-table-set-col-headers!
                     table
                     (append

commit be1ebb9d32246b18f3c5cab77d43e06db46c4625
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Thu Jan 4 20:24:43 2018 +1100

    [net-charts] use scheme rationals directly

diff --git a/gnucash/report/standard-reports/net-charts.scm b/gnucash/report/standard-reports/net-charts.scm
index 5c55fe4..6758826 100644
--- a/gnucash/report/standard-reports/net-charts.scm
+++ b/gnucash/report/standard-reports/net-charts.scm
@@ -256,7 +256,7 @@
     (define (monetary+ a b)
       (if (and (gnc:gnc-monetary? a) (gnc:gnc-monetary? b))
           (let ((same-currency? (gnc-commodity-equal (gnc:gnc-monetary-commodity a) (gnc:gnc-monetary-commodity b)))
-                (amount (gnc-numeric-add (gnc:gnc-monetary-amount a) (gnc:gnc-monetary-amount b) GNC-DENOM-AUTO GNC-RND-ROUND)))
+                (amount (+ (gnc:gnc-monetary-amount a) (gnc:gnc-monetary-amount b))))
             (if same-currency?
                 (gnc:make-gnc-monetary (gnc:gnc-monetary-commodity a) amount)
                 (warn "incompatible currencies in monetary+: " a b)))

commit 66488bbb1acf902c7304904f210240c9b43786dc
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Thu Jan 4 19:38:59 2018 +1100

    [net-charts] *reindent/whitespace*

diff --git a/gnucash/report/standard-reports/net-charts.scm b/gnucash/report/standard-reports/net-charts.scm
index 65a4997..5c55fe4 100644
--- a/gnucash/report/standard-reports/net-charts.scm
+++ b/gnucash/report/standard-reports/net-charts.scm
@@ -64,8 +64,6 @@
 ;;(define optname-x-grid (N_ "X grid"))
 (define optname-y-grid (N_ "Grid"))
 
-
-
 (define (options-generator inc-exp? linechart?)
   (let* ((options (gnc:new-options))
          ;; This is just a helper function for making options.
@@ -139,39 +137,38 @@
       "c" (N_ "Display a table of the selected data.")
       #f))
 
-    (gnc:options-add-plot-size! 
-     options gnc:pagename-display 
+    (gnc:options-add-plot-size!
+     options gnc:pagename-display
      optname-plot-width optname-plot-height "d" (cons 'percent 100.0) (cons 'percent 100.0))
 
     (if linechart?
         (begin
 
-     (add-option
-     (gnc:make-number-range-option
-      gnc:pagename-display optname-line-width
-      "e" opthelp-line-width
-      1.5 0.5 5 1 0.1 ))
+          (add-option
+           (gnc:make-number-range-option
+            gnc:pagename-display optname-line-width
+            "e" opthelp-line-width
+            1.5 0.5 5 1 0.1 ))
 
+          (add-option
+           (gnc:make-simple-boolean-option
+            gnc:pagename-display optname-y-grid
+            "f" (N_ "Add grid lines.")
+            #t))
 
-    (add-option
-     (gnc:make-simple-boolean-option
-      gnc:pagename-display optname-y-grid
-      "f" (N_ "Add grid lines.")
-      #t))
+          ;;(add-option
+          ;; (gnc:make-simple-boolean-option
+          ;;  gnc:pagename-display optname-x-grid
+          ;;  "g" (N_ "Add vertical grid lines.")
+          ;;  #f))
 
-    ;(add-option
-    ; (gnc:make-simple-boolean-option
-    ;  gnc:pagename-display optname-x-grid
-    ;  "g" (N_ "Add vertical grid lines.")
-    ;  #f))
-
-    (add-option
-     (gnc:make-simple-boolean-option
-      gnc:pagename-display optname-markers
-      "g" (N_ "Display a mark for each data point.")
-      #t))
+          (add-option
+           (gnc:make-simple-boolean-option
+            gnc:pagename-display optname-markers
+            "g" (N_ "Display a mark for each data point.")
+            #t))
 
-    ))
+          ))
 
     (gnc:options-set-default-section options gnc:pagename-general)
 
@@ -190,29 +187,25 @@
     (gnc:option-value
      (gnc:lookup-option (gnc:report-options report-obj) section name)))
 
-  (gnc:report-starting "reportname")
+  (gnc:report-starting "INC/EXP & A/L Charts")
   (let* ((to-date-t64 (gnc:time64-end-day-time
-                      (gnc:date-option-absolute-time
-                       (get-option gnc:pagename-general
-				   optname-to-date))))
+                       (gnc:date-option-absolute-time
+                        (get-option gnc:pagename-general
+                                    optname-to-date))))
          (from-date-t64 (gnc:time64-start-day-time
-                        (gnc:date-option-absolute-time
-                         (get-option gnc:pagename-general
-				     optname-from-date))))
+                         (gnc:date-option-absolute-time
+                          (get-option gnc:pagename-general
+                                      optname-from-date))))
          (interval (get-option gnc:pagename-general optname-stepsize))
-         (report-currency (get-option gnc:pagename-general
-                                      optname-report-currency))
-         (price-source (get-option gnc:pagename-general
-                                   optname-price-source))
-
+         (report-currency (get-option gnc:pagename-general optname-report-currency))
+         (price-source (get-option gnc:pagename-general optname-price-source))
          (accounts (get-option gnc:pagename-accounts optname-accounts))
-
-         (show-sep? (get-option gnc:pagename-display
-				(if inc-exp? optname-inc-exp
-				    optname-sep-bars)))
-         (show-net? (get-option gnc:pagename-display
-				(if inc-exp? optname-show-profit
-				    optname-net-bars)))
+         (show-sep? (get-option gnc:pagename-display (if inc-exp?
+                                                         optname-inc-exp
+                                                         optname-sep-bars)))
+         (show-net? (get-option gnc:pagename-display (if inc-exp?
+                                                         optname-show-profit
+                                                         optname-net-bars)))
          (height (get-option gnc:pagename-display optname-plot-height))
          (width (get-option gnc:pagename-display optname-plot-width))
          (markers (if linechart?
@@ -220,11 +213,9 @@
          (line-width (if linechart?
                          (get-option gnc:pagename-display optname-line-width)))
          (y-grid (if linechart? (get-option gnc:pagename-display optname-y-grid)))
-         ;;(x-grid (get-option gnc:pagename-display optname-x-grid))
-
+         ;;(x-grid (if linechart? (get-option gnc:pagename-display optname-x-grid)))
          (commodity-list #f)
          (exchange-fn #f)
-
          (dates-list ((if inc-exp?
                           gnc:make-date-interval-list
                           gnc:make-date-list)
@@ -234,8 +225,7 @@
                        from-date-t64)
                       (gnc:time64-end-day-time to-date-t64)
                       (gnc:deltasym-to-delta interval)))
-	 (report-title (get-option gnc:pagename-general
-                                  gnc:optname-reportname))
+         (report-title (get-option gnc:pagename-general gnc:optname-reportname))
          (classified-accounts (gnc:decompose-accountlist accounts))
          (show-table? (get-option gnc:pagename-display (N_ "Show table")))
          (document (gnc:make-html-document))
@@ -248,7 +238,8 @@
       (begin
         ((if linechart?
              gnc:html-linechart-append-column!
-             gnc:html-barchart-append-column!) chart data-list)
+             gnc:html-barchart-append-column!)
+         chart data-list)
         (if (gnc:not-all-zeros data-list) (set! non-zeros #t))
         #f))
 
@@ -256,7 +247,7 @@
     ;; 'report-currency' according to the exchange-fn. Returns a gnc:monetary
     (define (collector->monetary c date)
       (if (not (number? date))
-	  (throw 'wrong))
+          (throw 'wrong))
       (gnc:sum-collector-commodity
        c report-currency
        (lambda (a b) (exchange-fn a b date))))
@@ -310,86 +301,83 @@
     (set! exchange-fn (gnc:case-exchange-time-fn
                        price-source report-currency
                        commodity-list to-date-t64
-		       10 40))
+                       10 40))
     (gnc:report-percent-done 50)
 
     (if
      (not (null? accounts))
-     (let* ((assets-list #f)
-            (liability-list #f)
-            (net-list #f)
-	    (progress-range (cons 50 80))
+     (let* ((the-account-destination-alist
+             (if inc-exp?
+                 (append (map (lambda (account) (cons account 'asset))
+                              (assoc-ref classified-accounts ACCT-TYPE-INCOME))
+                         (map (lambda (account) (cons account 'liability))
+                              (assoc-ref classified-accounts ACCT-TYPE-EXPENSE)))
+                 (append  (map (lambda (account) (cons account 'asset))
+                               (assoc-ref classified-accounts ACCT-TYPE-ASSET))
+                          (map (lambda (account) (cons account 'liability))
+                               (assoc-ref classified-accounts ACCT-TYPE-LIABILITY)))))
+            (account-reformat (if inc-exp?
+                                  (lambda (account result)
+                                    (map (lambda (collector date-interval)
+                                           (gnc:monetary-neg (collector->monetary collector (second date-interval))))
+                                         result dates-list))
+                                  (lambda (account result)
+                                    (let ((commodity-collector (gnc:make-commodity-collector)))
+                                      (collector-end (fold (lambda (next date list-collector)
+                                                             (commodity-collector 'merge next #f)
+                                                             (collector-add list-collector
+                                                                            (collector->monetary
+                                                                             commodity-collector date)))
+                                                           (collector-into-list)
+                                                           result
+                                                           dates-list))))))
+            (work (category-by-account-report-work inc-exp?
+                                                   dates-list
+                                                   the-account-destination-alist
+                                                   (lambda (account date)
+                                                     (make-gnc-collector-collector))
+                                                   account-reformat))
+            (rpt (category-by-account-report-do-work work (cons 50 90)))
+            (assets (assoc-ref rpt 'asset))
+            (liabilities (assoc-ref rpt 'liability))
+            (assets-list (if assets
+                             (car assets)
+                             (map (lambda (d)
+                                    (gnc:make-gnc-monetary report-currency 0))
+                                  dates-list)))
+            (liability-list (if liabilities
+                                (car liabilities)
+                                (map (lambda (d)
+                                       (gnc:make-gnc-monetary report-currency 0))
+                                     dates-list)))
+            (net-list (map monetary+ assets-list liability-list))
             ;; Here the date strings for the x-axis labels are
             ;; created.
-            (date-iso-string-list '())
-            (save-fmt (qof-date-format-get)))
-
-       (define (datelist->stringlist dates-list)
-         (map (lambda (date-list-item)
-                (qof-print-date
-                 (if inc-exp?
-                     (car date-list-item)
-                     date-list-item)))
-              dates-list))
-
-       (define date-string-list
-         (if linechart?
-             (datelist->stringlist dates-list)
-             (map
-              (if inc-exp?
-                  (lambda (date-list-item)
-                    (qof-print-date
-                     (car date-list-item)))
-                  qof-print-date)
-              dates-list)))
-
-       (let* ((the-acount-destination-alist
-	       (if inc-exp?
-		   (append (map (lambda (account) (cons account 'asset))
-				 (assoc-ref classified-accounts ACCT-TYPE-INCOME))
-			   (map (lambda (account) (cons account 'liability))
-				 (assoc-ref classified-accounts ACCT-TYPE-EXPENSE)))
-		   (append  (map (lambda (account) (cons account 'asset))
-				 (assoc-ref classified-accounts ACCT-TYPE-ASSET))
-			    (map (lambda (account) (cons account 'liability))
-				 (assoc-ref classified-accounts ACCT-TYPE-LIABILITY)))))
-	      (account-reformat (if inc-exp?
-				    (lambda (account result)
-				      (map (lambda (collector date-interval)
-					     (gnc:monetary-neg (collector->monetary collector (second date-interval))))
-					   result dates-list))
-				    (lambda (account result)
-				      (let ((commodity-collector (gnc:make-commodity-collector)))
-					(collector-end (fold (lambda (next date list-collector)
-							       (commodity-collector 'merge next #f)
-							       (collector-add list-collector
-									      (collector->monetary
-									       commodity-collector date)))
-							     (collector-into-list)
-							     result
-							     dates-list))))))
-	      (work (category-by-account-report-work inc-exp?
-					  dates-list
-					  the-acount-destination-alist
-					  (lambda (account date)
-					    (make-gnc-collector-collector))
-					  account-reformat))
-	      (rpt (category-by-account-report-do-work work progress-range))
-	      (assets (assoc-ref rpt 'asset))
-	      (liabilities (assoc-ref rpt 'liability)))
-         (set! assets-list (if assets (car assets)
-                               (map (lambda (d)
-                                      (gnc:make-gnc-monetary report-currency 0))
-                                    dates-list)))
-         (set! liability-list (if liabilities (car liabilities)
-                                  (map (lambda (d)
-                                      (gnc:make-gnc-monetary report-currency 0))
-                                       dates-list)))
-	 )
-
-       (gnc:report-percent-done 80)
-       (set! net-list
-             (map monetary+ assets-list liability-list))
+            (datelist->stringlist (lambda (dates-list)
+                                    (map (lambda (date-list-item)
+                                           (qof-print-date
+                                            (if inc-exp?
+                                                (car date-list-item)
+                                                date-list-item)))
+                                         dates-list)))
+
+            (date-string-list (if linechart?
+                                  (datelist->stringlist dates-list)
+                                  (map
+                                   (if inc-exp?
+                                       (lambda (date-list-item)
+                                         (qof-print-date
+                                          (car date-list-item)))
+                                       qof-print-date)
+                                   dates-list)))
+            
+            (date-iso-string-list (let ((save-fmt (qof-date-format-get))
+                                        (retlist #f))
+                                    (qof-date-format-set QOF-DATE-FORMAT-ISO)
+                                    (set! retlist (datelist->stringlist dates-list))
+                                    (qof-date-format-set save-fmt)
+                                    retlist)))
+
        (gnc:report-percent-done 90)
 
        ((if linechart?
@@ -415,9 +403,6 @@
 
        (if linechart?
            (begin
-             (qof-date-format-set QOF-DATE-FORMAT-ISO)
-             (set! date-iso-string-list (datelist->stringlist dates-list))
-             (qof-date-format-set save-fmt)
              (gnc:html-linechart-set-row-labels! chart date-iso-string-list)
              (gnc:html-linechart-set-major-grid?! chart y-grid))
            (gnc:html-barchart-set-row-labels! chart date-string-list))
@@ -431,7 +416,7 @@
        (if show-sep?
            (begin
              (add-column! (map monetary->double assets-list))
-             (add-column!		      ;;(if inc-exp?
+             (add-column!                     ;;(if inc-exp?
               (map - (map monetary->double liability-list))
               ;;liability-list)
               )))
@@ -463,102 +448,102 @@
                (if show-net?
                    '("#2ECC40") '())))
 
-        ;; Set the line width and markers
+       ;; Set the line width and markers
        (if linechart?
            (begin
              (gnc:html-linechart-set-line-width! chart line-width)
              (gnc:html-linechart-set-markers?! chart markers)))
 
        ;; URLs for income/expense or asset/liabilities bars.
-;;       (if show-sep?
-;;           (let ((urls
-;;                  (list
-;;                   (gnc:make-report-anchor
-;;                    (if inc-exp?
-;;                        category-barchart-income-uuid
-;;                        category-barchart-asset-uuid)
-;;                    report-obj
-;;                    (list
-;;                     (list gnc:pagename-display
-;;                           (if linechart? "Use Stacked Lines" "Use Stacked Bars") #t)
-;;                     (list gnc:pagename-general
-;;                           gnc:optname-reportname
-;;                           (if inc-exp?
-;;                               (_ "Income Chart")
-;;                               (_ "Asset Chart")))))
-;;                   (gnc:make-report-anchor
-;;                    (if inc-exp?
-;;                        category-barchart-expense-uuid
-;;                        category-barchart-liability-uuid)
-;;                    report-obj
-;;                    (list
-;;                     (list gnc:pagename-display
-;;                           (if linechart? "Use Stacked Lines" "Use Stacked Bars") #t)
-;;                     (list gnc:pagename-general
-;;                           gnc:optname-reportname
-;;                           (if inc-exp?
-;;                               (_ "Expense Chart")
-;;                               (_ "Liability Chart"))))))))
-;;             ((if linechart?
-;;                  gnc:html-linechart-set-button-1-line-urls!
-;;                  gnc:html-barchart-set-button-1-line-urls!)
-;;              chart urls)
-;;             ((if linechart?
-;;                  gnc:html-linechart-set-button-1-legend-urls!
-;;                  gnc:html-barchart-set-button-1-legend-urls!)
-;;              chart urls)))
+       ;;       (if show-sep?
+       ;;           (let ((urls
+       ;;                  (list
+       ;;                   (gnc:make-report-anchor
+       ;;                    (if inc-exp?
+       ;;                        category-barchart-income-uuid
+       ;;                        category-barchart-asset-uuid)
+       ;;                    report-obj
+       ;;                    (list
+       ;;                     (list gnc:pagename-display
+       ;;                           (if linechart? "Use Stacked Lines" "Use Stacked Bars") #t)
+       ;;                     (list gnc:pagename-general
+       ;;                           gnc:optname-reportname
+       ;;                           (if inc-exp?
+       ;;                               (_ "Income Chart")
+       ;;                               (_ "Asset Chart")))))
+       ;;                   (gnc:make-report-anchor
+       ;;                    (if inc-exp?
+       ;;                        category-barchart-expense-uuid
+       ;;                        category-barchart-liability-uuid)
+       ;;                    report-obj
+       ;;                    (list
+       ;;                     (list gnc:pagename-display
+       ;;                           (if linechart? "Use Stacked Lines" "Use Stacked Bars") #t)
+       ;;                     (list gnc:pagename-general
+       ;;                           gnc:optname-reportname
+       ;;                           (if inc-exp?
+       ;;                               (_ "Expense Chart")
+       ;;                               (_ "Liability Chart"))))))))
+       ;;             ((if linechart?
+       ;;                  gnc:html-linechart-set-button-1-line-urls!
+       ;;                  gnc:html-barchart-set-button-1-line-urls!)
+       ;;              chart urls)
+       ;;             ((if linechart?
+       ;;                  gnc:html-linechart-set-button-1-legend-urls!
+       ;;                  gnc:html-barchart-set-button-1-legend-urls!)
+       ;;              chart urls)))
 
        ;; Test for all-zero data here.
        (if non-zeros
            (begin
-           (gnc:html-document-add-object! document chart)
+             (gnc:html-document-add-object! document chart)
              (if show-table?
-             (let ((table (gnc:make-html-table)))
-               (if linechart?
-                   (gnc:html-table-set-style!
-                    table "table"
-                    'attribute (list "border" 0)
-                    'attribute (list "cellspacing" 0)
-                    'attribute (list "cellpadding" 4)))
-                (gnc:html-table-set-col-headers!
-                 table
-                 (append
-                  (list (_ "Date"))
-                  (if show-sep?
-                      (if inc-exp?
-                          (list (_ "Income") (_ "Expense"))
-                          (list (_ "Assets") (_ "Liabilities")))
-                      '())
-                  (if show-net?
-                      (if inc-exp?
-                          (list (_ "Net Profit"))
-                          (list (_ "Net Worth")))
-                      '()))
-                 )
-               (gnc:html-table-append-column! table date-string-list)
-               (if show-sep?
-                   (begin
-                     (gnc:html-table-append-column! table assets-list)
-                     (gnc:html-table-append-column! table liability-list)
+                 (let ((table (gnc:make-html-table)))
+                   (if linechart?
+                       (gnc:html-table-set-style!
+                        table "table"
+                        'attribute (list "border" 0)
+                        'attribute (list "cellspacing" 0)
+                        'attribute (list "cellpadding" 4)))
+                   (gnc:html-table-set-col-headers!
+                    table
+                    (append
+                     (list (_ "Date"))
+                     (if show-sep?
+                         (if inc-exp?
+                             (list (_ "Income") (_ "Expense"))
+                             (list (_ "Assets") (_ "Liabilities")))
+                         '())
+                     (if show-net?
+                         (if inc-exp?
+                             (list (_ "Net Profit"))
+                             (list (_ "Net Worth")))
+                         '()))
                     )
-                   )
-               (if show-net?
-                   (gnc:html-table-append-column! table net-list)
-                   )
-               ;; set numeric columns to align right
-                (for-each
-                 (lambda (col)
-                   (gnc:html-table-set-col-style!
-                    table col "td"
-                    'attribute (list "class" "number-cell")))
-                 '(1 2 3))
-
-              (gnc:html-document-add-object! document table))
-             ))
+                   (gnc:html-table-append-column! table date-string-list)
+                   (if show-sep?
+                       (begin
+                         (gnc:html-table-append-column! table assets-list)
+                         (gnc:html-table-append-column! table liability-list)
+                         )
+                       )
+                   (if show-net?
+                       (gnc:html-table-append-column! table net-list)
+                       )
+                   ;; set numeric columns to align right
+                   (for-each
+                    (lambda (col)
+                      (gnc:html-table-set-col-style!
+                       table col "td"
+                       'attribute (list "class" "number-cell")))
+                    '(1 2 3))
+
+                   (gnc:html-document-add-object! document table))
+                 ))
            (gnc:html-document-add-object!
             document
             (gnc:html-make-empty-data-warning
-	     report-title (gnc:report-id report-obj)))))
+             report-title (gnc:report-id report-obj)))))
 
      ;; else no accounts selected
      (gnc:html-document-add-object!

commit ffd20b2e2f9a4078fb324b45d14daaff840c0b12
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Thu Jan 4 19:19:32 2018 +1100

    [net-charts] combine net-[bar|line]chart.scm into net-charts.scm
    
    This commit combines both charts into one. This can improve ease of
    maintenance.

diff --git a/gnucash/report/standard-reports/CMakeLists.txt b/gnucash/report/standard-reports/CMakeLists.txt
index fe7e8d3..adda1e4 100644
--- a/gnucash/report/standard-reports/CMakeLists.txt
+++ b/gnucash/report/standard-reports/CMakeLists.txt
@@ -23,8 +23,7 @@ set (standard_reports_SCHEME_2
     general-ledger.scm
     income-gst-statement.scm
     income-statement.scm
-    net-barchart.scm
-    net-linechart.scm
+    net-charts.scm
     portfolio.scm
     price-scatter.scm
     register.scm
diff --git a/gnucash/report/standard-reports/net-barchart.scm b/gnucash/report/standard-reports/net-barchart.scm
deleted file mode 100644
index dc3e5c2..0000000
--- a/gnucash/report/standard-reports/net-barchart.scm
+++ /dev/null
@@ -1,492 +0,0 @@
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; net-barchart.scm : Display a time series for either net worth or
-;; net profit.
-;;
-;; By Robert Merkel <rgmerk at mira.net>
-;; and Christian Stimming <stimming at tu-harburg.de>
-;;
-;; This program is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU General Public License as
-;; published by the Free Software Foundation; either version 2 of
-;; the License, or (at your option) any later version.
-;;
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-;;
-;; You should have received a copy of the GNU General Public License
-;; along with this program; if not, contact:
-;;
-;; Free Software Foundation           Voice:  +1-617-542-5942
-;; 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
-;; Boston, MA  02110-1301,  USA       gnu at gnu.org
-;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(define-module (gnucash report standard-reports net-barchart))
-
-(use-modules (srfi srfi-1))
-(use-modules (gnucash utilities)) 
-(use-modules (gnucash gnc-module))
-(use-modules (gnucash gettext))
-
-(use-modules (gnucash report report-system report-collectors))
-(use-modules (gnucash report report-system collectors))
-(use-modules (gnucash report standard-reports category-barchart)) ; for guids of called reports
-
-(gnc:module-load "gnucash/report/report-system" 0)
-
-(define reportname (N_ "Income/Expense Chart"))
-
-(define optname-from-date (N_ "Start Date"))
-(define optname-to-date (N_ "End Date"))
-(define optname-stepsize (N_ "Step Size"))
-(define optname-report-currency (N_ "Report's currency"))
-(define optname-price-source (N_ "Price Source"))
-
-(define optname-accounts (N_ "Accounts"))
-
-(define optname-inc-exp (N_ "Show Income/Expense"))
-(define optname-show-profit (N_ "Show Net Profit"))
-
-(define optname-sep-bars (N_ "Show Asset & Liability bars"))
-(define optname-net-bars (N_ "Show Net Worth bars"))
-
-(define optname-plot-width (N_ "Plot Width"))
-(define optname-plot-height (N_ "Plot Height"))
-
-(define (options-generator inc-exp?)
-  (let* ((options (gnc:new-options))
-         ;; This is just a helper function for making options.
-         ;; See libgnucash/scm/options.scm for details.
-         (add-option
-          (lambda (new-option)
-            (gnc:register-option options new-option))))
-
-    ;; General tab
-    (gnc:options-add-date-interval!
-     options gnc:pagename-general
-     optname-from-date optname-to-date "a")
-
-    (gnc:options-add-interval-choice!
-     options gnc:pagename-general optname-stepsize "b" 'MonthDelta)
-
-    (gnc:options-add-currency!
-     options gnc:pagename-general optname-report-currency "c")
-
-    (gnc:options-add-price-source!
-     options gnc:pagename-general
-     optname-price-source "d" 'weighted-average)
-
-    ;; Account tab
-    (add-option
-     (gnc:make-account-list-option
-      gnc:pagename-accounts optname-accounts
-      "a"
-      (N_ "Report on these accounts, if chosen account level allows.")
-      (lambda ()
-        (filter
-         (if inc-exp?
-             gnc:account-is-inc-exp?
-             (lambda (account) (not (gnc:account-is-inc-exp? account))))
-         (gnc-account-get-descendants-sorted (gnc-get-current-root-account))))
-      (lambda (accounts)
-        (list #t
-              (filter
-               (if inc-exp?
-                   gnc:account-is-inc-exp?
-                   (lambda (account)
-                     (not (gnc:account-is-inc-exp? account))))
-               accounts)))
-      #t))
-
-    ;; Display tab
-    (add-option
-     (gnc:make-simple-boolean-option
-      gnc:pagename-display
-      (if inc-exp? optname-inc-exp optname-sep-bars)
-      "a"
-      (if inc-exp?
-          (N_ "Show Income and Expenses?")
-          (N_ "Show the Asset and the Liability bars?"))
-      #t))
-
-    (add-option
-     (gnc:make-simple-boolean-option
-      gnc:pagename-display
-      (if inc-exp? optname-show-profit optname-net-bars)
-      "b"
-      (if inc-exp?
-          (N_ "Show the net profit?")
-          (N_ "Show a Net Worth bar?"))
-      #t))
-
-    (add-option
-     (gnc:make-simple-boolean-option
-      gnc:pagename-display
-      (N_ "Show table")
-      "c" (N_ "Display a table of the selected data.")
-      #f))
-
-    (gnc:options-add-plot-size! 
-     options gnc:pagename-display 
-     optname-plot-width optname-plot-height "d" (cons 'percent 100.0) (cons 'percent 100.0))
-
-    (gnc:options-set-default-section options gnc:pagename-general)
-
-    options))
-
-;; This is the rendering function. It accepts a database of options
-;; and generates an object of type <html-document>.  See the file
-;; report-html.txt for documentation; the file report-html.scm
-;; includes all the relevant Scheme code. The option database passed
-;; to the function is one created by the options-generator function
-;; defined above.
-(define (net-renderer report-obj inc-exp?)
-
-  ;; This is a helper function for looking up option values.
-  (define (get-option section name)
-    (gnc:option-value
-     (gnc:lookup-option (gnc:report-options report-obj) section name)))
-
-  (gnc:report-starting reportname)
-  (let* ((to-date-t64 (gnc:time64-end-day-time
-                      (gnc:date-option-absolute-time
-                       (get-option gnc:pagename-general
-				   optname-to-date))))
-         (from-date-t64 (gnc:time64-start-day-time
-                        (gnc:date-option-absolute-time
-                         (get-option gnc:pagename-general
-				     optname-from-date))))
-         (interval (get-option gnc:pagename-general optname-stepsize))
-         (report-currency (get-option gnc:pagename-general
-                                      optname-report-currency))
-         (price-source (get-option gnc:pagename-general
-                                   optname-price-source))
-
-         (accounts (get-option gnc:pagename-accounts optname-accounts))
-
-         (show-sep? (get-option gnc:pagename-display
-				(if inc-exp? optname-inc-exp
-				    optname-sep-bars)))
-         (show-net? (get-option gnc:pagename-display
-				(if inc-exp? optname-show-profit
-				    optname-net-bars)))
-         (height (get-option gnc:pagename-display optname-plot-height))
-         (width (get-option gnc:pagename-display optname-plot-width))
-
-         (commodity-list #f)
-         (exchange-fn #f)
-
-         (dates-list ((if inc-exp?
-                          gnc:make-date-interval-list
-                          gnc:make-date-list)
-                      ((if inc-exp?
-                           gnc:time64-start-day-time
-                           gnc:time64-end-day-time)
-                       from-date-t64)
-                      (gnc:time64-end-day-time to-date-t64)
-                      (gnc:deltasym-to-delta interval)))
-	 (report-title (get-option gnc:pagename-general
-                                  gnc:optname-reportname))
-         (classified-accounts (gnc:decompose-accountlist accounts))
-         (show-table? (get-option gnc:pagename-display (N_ "Show table")))
-         (document (gnc:make-html-document))
-         (chart (gnc:make-html-barchart))
-         (non-zeros #f))
-
-    (define (add-column! data-list)
-      (begin
-        (gnc:html-barchart-append-column! chart data-list)
-        (if (gnc:not-all-zeros data-list) (set! non-zeros #t))
-        #f))
-
-    ;; This exchanges the commodity-collector 'c' to one single
-    ;; 'report-currency' according to the exchange-fn. Returns a gnc:monetary
-    (define (collector->monetary c date)
-      (if (not (number? date))
-	  (throw 'wrong))
-      (gnc:sum-collector-commodity
-       c report-currency
-       (lambda (a b) (exchange-fn a b date))))
-
-    ;; Add two gnc-monetary objects in the same currency.
-    (define (monetary+ a b)
-      (if (and (gnc:gnc-monetary? a) (gnc:gnc-monetary? b))
-	  (let ((same-currency? (gnc-commodity-equal (gnc:gnc-monetary-commodity a) (gnc:gnc-monetary-commodity b)))
-		(amount (gnc-numeric-add (gnc:gnc-monetary-amount a) (gnc:gnc-monetary-amount b) GNC-DENOM-AUTO GNC-RND-ROUND)))
-	    (if same-currency?
-		(gnc:make-gnc-monetary (gnc:gnc-monetary-commodity a) amount)
-		(warn "incompatible currencies in monetary+: " a b)))
-	  (warn "wrong arguments for monetary+: " a b)))
-
-    (define (monetary->double monetary)
-      (gnc-numeric-to-double (gnc:gnc-monetary-amount monetary)))
-
-    ;; This calculates the balances for all the 'accounts' for each
-    ;; element of the list 'dates'. If income?==#t, the signs get
-    ;; reversed according to income-sign-reverse general option
-    ;; settings. Uses the collector->monetary conversion function
-    ;; above. Returns a list of gnc-monetary.
-    (define (process-datelist accounts dates income?)
-      (map
-       (lambda (date)
-         (collector->monetary
-          ((if inc-exp?
-               (if income?
-                   gnc:accounts-get-comm-total-income
-                   gnc:accounts-get-comm-total-expense)
-               gnc:accounts-get-comm-total-assets)
-           accounts
-           (lambda (account)
-             (if inc-exp?
-                 ;; for inc-exp, 'date' is a pair of time values, else
-                 ;; it is a time value.
-                 (gnc:account-get-comm-balance-interval
-                  account (first date) (second date) #f)
-                 (gnc:account-get-comm-balance-at-date
-                  account date #f))))
-          (if inc-exp? (second date) date)))
-       dates))
-
-    (gnc:report-percent-done 1)
-    (set! commodity-list (gnc:accounts-get-commodities
-                          (append
-                           (gnc:acccounts-get-all-subaccounts accounts)
-                           accounts)
-                          report-currency))
-    (gnc:report-percent-done 10)
-    (set! exchange-fn (gnc:case-exchange-time-fn
-                       price-source report-currency
-                       commodity-list to-date-t64
-		       10 40))
-    (gnc:report-percent-done 50)
-
-    (if
-     (not (null? accounts))
-     (let* ((assets-list #f)
-            (liability-list #f)
-            (net-list #f)
-	    (progress-range (cons 50 80))
-            (date-string-list (map
-                               (if inc-exp?
-                                   (lambda (date-list-item)
-                                     (qof-print-date
-                                      (car date-list-item)))
-                                   qof-print-date)
-                               dates-list)))
-       (let* ((the-acount-destination-alist
-	       (if inc-exp?
-		   (append (map (lambda (account) (cons account 'asset))
-				 (assoc-ref classified-accounts ACCT-TYPE-INCOME))
-			   (map (lambda (account) (cons account 'liability))
-				 (assoc-ref classified-accounts ACCT-TYPE-EXPENSE)))
-		   (append  (map (lambda (account) (cons account 'asset))
-				 (assoc-ref classified-accounts ACCT-TYPE-ASSET))
-			    (map (lambda (account) (cons account 'liability))
-				 (assoc-ref classified-accounts ACCT-TYPE-LIABILITY)))))
-	      (account-reformat (if inc-exp?
-				    (lambda (account result)
-				      (map (lambda (collector date-interval)
-					     (gnc:monetary-neg (collector->monetary collector (second date-interval))))
-					   result dates-list))
-				    (lambda (account result)
-				      (let ((commodity-collector (gnc:make-commodity-collector)))
-					(collector-end (fold (lambda (next date list-collector)
-							       (commodity-collector 'merge next #f)
-							       (collector-add list-collector
-									      (collector->monetary
-									       commodity-collector date)))
-							     (collector-into-list)
-							     result
-							     dates-list))))))
-	      (work (category-by-account-report-work inc-exp?
-					  dates-list
-					  the-acount-destination-alist
-					  (lambda (account date)
-					    (make-gnc-collector-collector))
-					  account-reformat))
-	      (rpt (category-by-account-report-do-work work progress-range))
-	      (assets (assoc-ref rpt 'asset))
-	      (liabilities (assoc-ref rpt 'liability)))
-	 (set! assets-list (if assets (car assets)
-			       (map (lambda (d)
-				      (gnc:make-gnc-monetary report-currency 0/1))
-				    dates-list)))
-	 (set! liability-list (if liabilities (car liabilities)
-				  (map (lambda (d)
-				      (gnc:make-gnc-monetary report-currency 0/1))
-				    dates-list)))
-	 )
-
-       (gnc:report-percent-done 80)
-       (set! net-list
-             (map monetary+ assets-list liability-list))
-       (gnc:report-percent-done 90)
-
-       (gnc:html-barchart-set-title!
-        chart report-title)
-       (gnc:html-barchart-set-subtitle!
-        chart (format #f
-                       (_ "~a to ~a")
-                       (gnc:html-string-sanitize (qof-print-date from-date-t64))
-                       (gnc:html-string-sanitize (qof-print-date to-date-t64))))
-       (gnc:html-barchart-set-width! chart width)
-       (gnc:html-barchart-set-height! chart height)
-       (gnc:html-barchart-set-row-labels! chart date-string-list)
-       (gnc:html-barchart-set-y-axis-label!
-        chart (gnc-commodity-get-mnemonic report-currency))
-       ;; Determine whether we have enough space for horizontal labels
-       ;; -- kind of a hack. Assumptions: y-axis labels and legend
-       ;; require 200 pixels, and each x-axes label needs 60 pixels.
-       ;;(gnc:html-barchart-set-row-labels-rotated?!
-       ;; chart (< (/ (- width 200)
-       ;;             (length date-string-list)) 60))
-
-       ;; Add the data
-       (if show-sep?
-           (begin
-             (add-column! (map monetary->double assets-list))
-             (add-column!		      ;;(if inc-exp?
-              (map - (map monetary->double liability-list))
-              ;;liability-list)
-              )))
-       (if show-net?
-           (add-column! (map monetary->double net-list)))
-
-       ;; Legend labels, colors
-       (gnc:html-barchart-set-col-labels!
-        chart (append
-               (if show-sep?
-                   (if inc-exp?
-                       (list (_ "Income") (_ "Expense"))
-                       (list (_ "Assets") (_ "Liabilities")))
-                   '())
-               (if show-net?
-                   (if inc-exp?
-                       (list (_ "Net Profit"))
-                       (list (_ "Net Worth")))
-                   '())))
-       (gnc:html-barchart-set-col-colors!
-        chart (append
-               (if show-sep?
-                   '("#0074D9" "#FF4136") '())
-               (if show-net?
-                   '("#2ECC40") '())))
-
-       ;; URLs for income/expense or asset/liabilities bars.
-;;       (if show-sep?
-;;           (let ((urls
-;;                  (list
-;;                   (gnc:make-report-anchor
-;;                    (if inc-exp?
-;;                        category-barchart-income-uuid
-;;                        category-barchart-asset-uuid)
-;;                    report-obj
-;;                    (list
-;;                     (list gnc:pagename-display
-;;                           "Use Stacked Bars" #t)
-;;                     (list gnc:pagename-general
-;;                           gnc:optname-reportname
-;;                           (if inc-exp?
-;;                               (_ "Income Chart")
-;;                               (_ "Asset Chart")))))
-;;                   (gnc:make-report-anchor
-;;                    (if inc-exp?
-;;                        category-barchart-expense-uuid
-;;                        category-barchart-liability-uuid)
-;;                    report-obj
-;;                    (list
-;;                     (list gnc:pagename-display
-;;                           "Use Stacked Bars" #t)
-;;                     (list gnc:pagename-general
-;;                           gnc:optname-reportname
-;;                           (if inc-exp?
-;;                               (_ "Expense Chart")
-;;                               (_ "Liability Chart"))))))))
-;;             (gnc:html-barchart-set-button-1-bar-urls!
-;;              chart urls)
-;;             (gnc:html-barchart-set-button-1-legend-urls!
-;;              chart urls)))
-
-       ;; Test for all-zero data here.
-       (if non-zeros
-           (begin
-           (gnc:html-document-add-object! document chart)
-             (if show-table?
-             (let ((table (gnc:make-html-table)))
-                (gnc:html-table-set-col-headers!
-                 table
-                 (append
-                  (list (_ "Date"))
-                  (if show-sep?
-                      (if inc-exp?
-                          (list (_ "Income") (_ "Expense"))
-                          (list (_ "Assets") (_ "Liabilities")))
-                      '())
-                  (if show-net?
-                      (if inc-exp?
-                          (list (_ "Net Profit"))
-                          (list (_ "Net Worth")))
-                      '()))
-                 )
-               (gnc:html-table-append-column! table date-string-list)
-               (if show-sep?
-                   (begin
-                     (gnc:html-table-append-column! table assets-list)
-                     (gnc:html-table-append-column! table liability-list)
-                    )
-                   )
-               (if show-net?
-                   (gnc:html-table-append-column! table net-list)
-                   )
-               ;; set numeric columns to align right
-                (for-each
-                 (lambda (col)
-                   (gnc:html-table-set-col-style!
-                    table col "td"
-                    'attribute (list "class" "number-cell")))
-                 '(1 2 3))
-
-              (gnc:html-document-add-object! document table))
-             ))
-           (gnc:html-document-add-object!
-            document
-            (gnc:html-make-empty-data-warning
-	     report-title (gnc:report-id report-obj)))))
-
-     ;; else no accounts selected
-     (gnc:html-document-add-object!
-      document
-      (gnc:html-make-no-account-warning
-       report-title (gnc:report-id report-obj))))
-
-    (gnc:report-finished)
-    document))
-
-;; Export reports
-
-(export net-worth-barchart-uuid)
-(export income-expense-barchart-uuid)
-
-(define net-worth-barchart-uuid "cbba1696c8c24744848062c7f1cf4a72")
-(define income-expense-barchart-uuid "80769921e87943adade887b9835a7685")
-
-;; Here we define the actual report
-(gnc:define-report
- 'version 1
- 'name (N_ "Net Worth Barchart")
- 'report-guid net-worth-barchart-uuid
- 'menu-path (list gnc:menuname-asset-liability)
- 'options-generator (lambda () (options-generator #f))
- 'renderer (lambda (report-obj) (net-renderer report-obj #f)))
-
-(gnc:define-report
- 'version 1
- 'name reportname
- 'report-guid income-expense-barchart-uuid
- 'menu-name (N_ "Income & Expense Barchart")
- 'menu-path (list gnc:menuname-income-expense)
- 'options-generator (lambda () (options-generator #t))
- 'renderer (lambda (report-obj) (net-renderer report-obj #t)))
diff --git a/gnucash/report/standard-reports/net-linechart.scm b/gnucash/report/standard-reports/net-charts.scm
similarity index 78%
rename from gnucash/report/standard-reports/net-linechart.scm
rename to gnucash/report/standard-reports/net-charts.scm
index ec8f23a..65a4997 100644
--- a/gnucash/report/standard-reports/net-linechart.scm
+++ b/gnucash/report/standard-reports/net-charts.scm
@@ -1,10 +1,11 @@
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; net-linechart.scm : Display a time series line chart for
+;; net-charts.scm : Display a time series line or bar chart for
 ;; either net worth or net profit.
 ;;
 ;; By Robert Merkel <rgmerk at mira.net>
 ;; and Christian Stimming <stimming at tu-harburg.de>
 ;; and Mike Evans <mikee at saxicooa.co.uk>
+;; and Christopher Lam to combine with net-barchart.scm
 ;;
 ;; This program is free software; you can redistribute it and/or
 ;; modify it under the terms of the GNU General Public License as
@@ -25,7 +26,7 @@
 ;;
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(define-module (gnucash report standard-reports net-linechart))
+(define-module (gnucash report standard-reports net-charts))
 
 (use-modules (srfi srfi-1))
 (use-modules (gnucash utilities)) 
@@ -37,7 +38,6 @@
 (use-modules (gnucash report standard-reports category-barchart)) ; for guids of called reports
 (gnc:module-load "gnucash/report/report-system" 0)
 
-(define reportname (N_ "Income/Expense Chart"))
 
 (define optname-from-date (N_ "Start Date"))
 (define optname-to-date (N_ "End Date"))
@@ -66,7 +66,7 @@
 
 
 
-(define (options-generator inc-exp?)
+(define (options-generator inc-exp? linechart?)
   (let* ((options (gnc:new-options))
          ;; This is just a helper function for making options.
          ;; See libgnucash/scm/options.scm for details.
@@ -143,6 +143,8 @@
      options gnc:pagename-display 
      optname-plot-width optname-plot-height "d" (cons 'percent 100.0) (cons 'percent 100.0))
 
+    (if linechart?
+        (begin
 
      (add-option
      (gnc:make-number-range-option
@@ -169,6 +171,8 @@
       "g" (N_ "Display a mark for each data point.")
       #t))
 
+    ))
+
     (gnc:options-set-default-section options gnc:pagename-general)
 
     options))
@@ -179,14 +183,14 @@
 ;; includes all the relevant Scheme code. The option database passed
 ;; to the function is one created by the options-generator function
 ;; defined above.
-(define (net-renderer report-obj inc-exp?)
+(define (net-renderer report-obj inc-exp? linechart?)
 
   ;; This is a helper function for looking up option values.
   (define (get-option section name)
     (gnc:option-value
      (gnc:lookup-option (gnc:report-options report-obj) section name)))
 
-  (gnc:report-starting reportname)
+  (gnc:report-starting "reportname")
   (let* ((to-date-t64 (gnc:time64-end-day-time
                       (gnc:date-option-absolute-time
                        (get-option gnc:pagename-general
@@ -211,11 +215,12 @@
 				    optname-net-bars)))
          (height (get-option gnc:pagename-display optname-plot-height))
          (width (get-option gnc:pagename-display optname-plot-width))
-        (markers (get-option gnc:pagename-display optname-markers))
-
-        (line-width (get-option gnc:pagename-display optname-line-width))
-        (y-grid (get-option gnc:pagename-display optname-y-grid))
-        ;(x-grid (get-option gnc:pagename-display optname-x-grid))
+         (markers (if linechart?
+                      (get-option gnc:pagename-display optname-markers)))
+         (line-width (if linechart?
+                         (get-option gnc:pagename-display optname-line-width)))
+         (y-grid (if linechart? (get-option gnc:pagename-display optname-y-grid)))
+         ;;(x-grid (get-option gnc:pagename-display optname-x-grid))
 
          (commodity-list #f)
          (exchange-fn #f)
@@ -225,7 +230,8 @@
                           gnc:make-date-list)
                       ((if inc-exp?
                            gnc:time64-start-day-time
-                           gnc:time64-end-day-time) from-date-t64)
+                           gnc:time64-end-day-time)
+                       from-date-t64)
                       (gnc:time64-end-day-time to-date-t64)
                       (gnc:deltasym-to-delta interval)))
 	 (report-title (get-option gnc:pagename-general
@@ -233,12 +239,16 @@
          (classified-accounts (gnc:decompose-accountlist accounts))
          (show-table? (get-option gnc:pagename-display (N_ "Show table")))
          (document (gnc:make-html-document))
-         (chart (gnc:make-html-linechart))
+         (chart (if linechart?
+                    (gnc:make-html-linechart)
+                    (gnc:make-html-barchart)))
          (non-zeros #f))
 
     (define (add-column! data-list)
       (begin
-        (gnc:html-linechart-append-column! chart data-list)
+        ((if linechart?
+             gnc:html-linechart-append-column!
+             gnc:html-barchart-append-column!) chart data-list)
         (if (gnc:not-all-zeros data-list) (set! non-zeros #t))
         #f))
 
@@ -311,18 +321,28 @@
 	    (progress-range (cons 50 80))
             ;; Here the date strings for the x-axis labels are
             ;; created.
-            (date-string-list '())
             (date-iso-string-list '())
             (save-fmt (qof-date-format-get)))
 
        (define (datelist->stringlist dates-list)
          (map (lambda (date-list-item)
-                      (qof-print-date
-                       (if inc-exp?
-                           (car date-list-item)
-                           date-list-item)))
+                (qof-print-date
+                 (if inc-exp?
+                     (car date-list-item)
+                     date-list-item)))
               dates-list))
 
+       (define date-string-list
+         (if linechart?
+             (datelist->stringlist dates-list)
+             (map
+              (if inc-exp?
+                  (lambda (date-list-item)
+                    (qof-print-date
+                     (car date-list-item)))
+                  qof-print-date)
+              dates-list)))
+
        (let* ((the-acount-destination-alist
 	       (if inc-exp?
 		   (append (map (lambda (account) (cons account 'asset))
@@ -359,11 +379,11 @@
 	      (liabilities (assoc-ref rpt 'liability)))
          (set! assets-list (if assets (car assets)
                                (map (lambda (d)
-                                      (gnc:make-gnc-monetary report-currency (gnc-numeric-zero)))
+                                      (gnc:make-gnc-monetary report-currency 0))
                                     dates-list)))
          (set! liability-list (if liabilities (car liabilities)
                                   (map (lambda (d)
-                                      (gnc:make-gnc-monetary report-currency (gnc-numeric-zero)))
+                                      (gnc:make-gnc-monetary report-currency 0))
                                        dates-list)))
 	 )
 
@@ -372,23 +392,39 @@
              (map monetary+ assets-list liability-list))
        (gnc:report-percent-done 90)
 
-       (gnc:html-linechart-set-title!
+       ((if linechart?
+            gnc:html-linechart-set-title!
+            gnc:html-barchart-set-title!)
         chart report-title)
-       (gnc:html-linechart-set-subtitle!
+
+       ((if linechart?
+            gnc:html-linechart-set-subtitle!
+            gnc:html-barchart-set-subtitle!)
         chart (format #f
                        (_ "~a to ~a")
                        (qof-print-date from-date-t64)
                        (qof-print-date to-date-t64)))
-       (gnc:html-linechart-set-width! chart width)
-       (gnc:html-linechart-set-height! chart height)
 
-       (qof-date-format-set QOF-DATE-FORMAT-ISO)
-       (set! date-iso-string-list (datelist->stringlist dates-list))
-       (qof-date-format-set save-fmt)
-       (gnc:html-linechart-set-row-labels! chart date-iso-string-list)
+       ((if linechart?
+            gnc:html-linechart-set-width!
+            gnc:html-barchart-set-width!) chart width)
+
+       ((if linechart?
+            gnc:html-linechart-set-height!
+            gnc:html-barchart-set-height!) chart height)
 
-       (gnc:html-linechart-set-major-grid?! chart y-grid)
-       (gnc:html-linechart-set-y-axis-label!
+       (if linechart?
+           (begin
+             (qof-date-format-set QOF-DATE-FORMAT-ISO)
+             (set! date-iso-string-list (datelist->stringlist dates-list))
+             (qof-date-format-set save-fmt)
+             (gnc:html-linechart-set-row-labels! chart date-iso-string-list)
+             (gnc:html-linechart-set-major-grid?! chart y-grid))
+           (gnc:html-barchart-set-row-labels! chart date-string-list))
+
+       ((if linechart?
+            gnc:html-linechart-set-y-axis-label!
+            gnc:html-barchart-set-y-axis-label!)
         chart (gnc-commodity-get-mnemonic report-currency))
 
        ;; Add the data
@@ -403,7 +439,9 @@
            (add-column! (map monetary->double net-list)))
 
        ;; Legend labels, colors
-       (gnc:html-linechart-set-col-labels!
+       ((if linechart?
+            gnc:html-linechart-set-col-labels!
+            gnc:html-barchart-set-col-labels!)
         chart (append
                (if show-sep?
                    (if inc-exp?
@@ -415,7 +453,10 @@
                        (list (_ "Net Profit"))
                        (list (_ "Net Worth")))
                    '())))
-       (gnc:html-linechart-set-col-colors!
+
+       ((if linechart?
+            gnc:html-linechart-set-col-colors!
+            gnc:html-barchart-set-col-colors!)
         chart (append
                (if show-sep?
                    '("#0074D9" "#FF4136") '())
@@ -423,10 +464,10 @@
                    '("#2ECC40") '())))
 
         ;; Set the line width and markers
-        (gnc:html-linechart-set-line-width!
-            chart line-width)
-        (gnc:html-linechart-set-markers?!
-            chart markers)
+       (if linechart?
+           (begin
+             (gnc:html-linechart-set-line-width! chart line-width)
+             (gnc:html-linechart-set-markers?! chart markers)))
 
        ;; URLs for income/expense or asset/liabilities bars.
 ;;       (if show-sep?
@@ -439,7 +480,7 @@
 ;;                    report-obj
 ;;                    (list
 ;;                     (list gnc:pagename-display
-;;                           "Use Stacked Lines" #t)
+;;                           (if linechart? "Use Stacked Lines" "Use Stacked Bars") #t)
 ;;                     (list gnc:pagename-general
 ;;                           gnc:optname-reportname
 ;;                           (if inc-exp?
@@ -452,15 +493,19 @@
 ;;                    report-obj
 ;;                    (list
 ;;                     (list gnc:pagename-display
-;;                           "Use Stacked Lines" #t)
+;;                           (if linechart? "Use Stacked Lines" "Use Stacked Bars") #t)
 ;;                     (list gnc:pagename-general
 ;;                           gnc:optname-reportname
 ;;                           (if inc-exp?
 ;;                               (_ "Expense Chart")
 ;;                               (_ "Liability Chart"))))))))
-;;             (gnc:html-linechart-set-button-1-line-urls!
+;;             ((if linechart?
+;;                  gnc:html-linechart-set-button-1-line-urls!
+;;                  gnc:html-barchart-set-button-1-line-urls!)
 ;;              chart urls)
-;;             (gnc:html-linechart-set-button-1-legend-urls!
+;;             ((if linechart?
+;;                  gnc:html-linechart-set-button-1-legend-urls!
+;;                  gnc:html-barchart-set-button-1-legend-urls!)
 ;;              chart urls)))
 
        ;; Test for all-zero data here.
@@ -469,11 +514,12 @@
            (gnc:html-document-add-object! document chart)
              (if show-table?
              (let ((table (gnc:make-html-table)))
-                (gnc:html-table-set-style!
-                  table "table"
-                  'attribute (list "border" 0)
-                  'attribute (list "cellspacing" 0)
-                  'attribute (list "cellpadding" 4))
+               (if linechart?
+                   (gnc:html-table-set-style!
+                    table "table"
+                    'attribute (list "border" 0)
+                    'attribute (list "cellspacing" 0)
+                    'attribute (list "cellpadding" 4)))
                 (gnc:html-table-set-col-headers!
                  table
                  (append
@@ -489,7 +535,6 @@
                           (list (_ "Net Worth")))
                       '()))
                  )
-               (set! date-string-list (datelist->stringlist dates-list))
                (gnc:html-table-append-column! table date-string-list)
                (if show-sep?
                    (begin
@@ -526,25 +571,47 @@
 
 ;; Export reports
 
+(export net-worth-barchart-uuid)
 (export net-worth-linechart-uuid)
+(export income-expense-barchart-uuid)
+
 (define net-worth-linechart-uuid "d8b63264186b11e19038001558291366")
+(define net-worth-barchart-uuid "cbba1696c8c24744848062c7f1cf4a72")
+(define income-expense-barchart-uuid "80769921e87943adade887b9835a7685")
 
 ;; Here we define the actual report
 (gnc:define-report
  'version 1
+ 'name (N_ "Net Worth Barchart")
+ 'report-guid net-worth-barchart-uuid
+ 'menu-path (list gnc:menuname-asset-liability)
+ 'options-generator (lambda () (options-generator #f #f))
+ 'renderer (lambda (report-obj) (net-renderer report-obj #f #f)))
+
+(gnc:define-report
+ 'version 1
+ 'name (N_ "Income/Expense Chart")
+ 'report-guid income-expense-barchart-uuid
+ 'menu-name (N_ "Income & Expense Barchart")
+ 'menu-path (list gnc:menuname-income-expense)
+ 'options-generator (lambda () (options-generator #t #f))
+ 'renderer (lambda (report-obj) (net-renderer report-obj #t #f)))
+
+(gnc:define-report
+ 'version 1
  'name (N_ "Net Worth Linechart")
  'report-guid net-worth-linechart-uuid
  'menu-path (list gnc:menuname-asset-liability)
- 'options-generator (lambda () (options-generator #f))
- 'renderer (lambda (report-obj) (net-renderer report-obj #f)))
+ 'options-generator (lambda () (options-generator #f #t))
+ 'renderer (lambda (report-obj) (net-renderer report-obj #f #t)))
 
 ;; Not sure if a line chart makes sense for Income & Expense
 ;; Feel free to uncomment and try it though
 (gnc:define-report
  'version 1
- 'name reportname
+ 'name (N_ "Income & Expense Linechart")
  'report-guid "e533c998186b11e1b2e2001558291366"
  'menu-name (N_ "Income & Expense Linechart")
  'menu-path (list gnc:menuname-income-expense)
- 'options-generator (lambda () (options-generator #t))
- 'renderer (lambda (report-obj) (net-renderer report-obj #t)))
+ 'options-generator (lambda () (options-generator #t #t))
+ 'renderer (lambda (report-obj) (net-renderer report-obj #t #t)))
diff --git a/gnucash/report/standard-reports/test/test-net-charts.scm b/gnucash/report/standard-reports/test/test-net-charts.scm
index 32e7578..74e8466 100644
--- a/gnucash/report/standard-reports/test/test-net-charts.scm
+++ b/gnucash/report/standard-reports/test/test-net-charts.scm
@@ -1,8 +1,7 @@
 (use-modules (gnucash gnc-module))
 (gnc:module-begin-syntax (gnc:module-load "gnucash/app-utils" 0))
 (use-modules (gnucash engine test test-extras))
-(use-modules (gnucash report standard-reports net-barchart))
-(use-modules (gnucash report standard-reports net-linechart))
+(use-modules (gnucash report standard-reports net-charts))
 (use-modules (gnucash report stylesheets))
 (use-modules (gnucash report report-system))
 (use-modules (gnucash report report-system test test-extras))
diff --git a/gnucash/report/standard-reports/test/test-standard-category-report.scm b/gnucash/report/standard-reports/test/test-standard-category-report.scm
index 32c9fda..cdb2641 100644
--- a/gnucash/report/standard-reports/test/test-standard-category-report.scm
+++ b/gnucash/report/standard-reports/test/test-standard-category-report.scm
@@ -35,7 +35,7 @@
 (use-modules (gnucash app-utils))
 (use-modules (gnucash engine))
 (use-modules (sw_engine))
-(use-modules (gnucash report standard-reports net-barchart))
+(use-modules (gnucash report standard-reports net-charts))
 
 (use-modules (gnucash report report-system test test-extras))
 
diff --git a/gnucash/report/standard-reports/test/test-standard-net-barchart.scm b/gnucash/report/standard-reports/test/test-standard-net-barchart.scm
index 825c92c..0ef9fd5 100644
--- a/gnucash/report/standard-reports/test/test-standard-net-barchart.scm
+++ b/gnucash/report/standard-reports/test/test-standard-net-barchart.scm
@@ -30,7 +30,7 @@
 (use-modules (gnucash report report-system test test-extras))
 
 (use-modules (gnucash report standard-reports test test-generic-net-barchart))
-(use-modules (gnucash report standard-reports net-barchart))
+(use-modules (gnucash report standard-reports net-charts))
 
 ;; Explicitly set locale to make the report output predictable
 (setlocale LC_ALL "C")
diff --git a/gnucash/report/standard-reports/test/test-standard-net-linechart.scm b/gnucash/report/standard-reports/test/test-standard-net-linechart.scm
index d74387b..1270c69 100644
--- a/gnucash/report/standard-reports/test/test-standard-net-linechart.scm
+++ b/gnucash/report/standard-reports/test/test-standard-net-linechart.scm
@@ -36,7 +36,7 @@
 (use-modules (gnucash report report-system test test-extras))
 
 (use-modules (gnucash report standard-reports test test-generic-net-linechart))
-(use-modules (gnucash report standard-reports net-linechart))
+(use-modules (gnucash report standard-reports net-charts))
 
 ;; Explicitly set locale to make the report output predictable
 (setlocale LC_ALL "C")

commit 8cae602e66f593bb2af1c5bbcbd3e48476a58596
Author: Benjamin Gordon <ben at bxg.org>
Date:   Tue Jun 12 23:19:48 2018 -0600

    Add support for libsecret to cmake
    
    gnucash has historically supported storing passwords for database
    backends with libsecret when HAVE_LIBSECRET is defined. The code is
    still present, but support for detecting libsecret's availablity was not
    ported over when the build system was converted to cmake.  This change
    restores the missing detection.

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 989d579..8207055 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -505,6 +505,11 @@ find_program(POD2MAN_EXECUTABLE pod2man HINTS ${PERL_DIR})
 gnc_pkg_check_modules (ICU4C REQUIRED icu-uc)
 gnc_pkg_check_modules (ICU4C_I18N REQUIRED icu-i18n)
 
+GNC_PKG_CHECK_MODULES (LIBSECRET libsecret-1>=0.18)
+IF (LIBSECRET_FOUND)
+  SET (HAVE_LIBSECRET ON)
+ENDIF (LIBSECRET_FOUND)
+
 #BOOST
 set (Boost_USE_MULTITHREADED ON)
 set (Boost_FIND_QUIETLY ON)
diff --git a/cmake/README_CMAKE.txt b/cmake/README_CMAKE.txt
index 8d2ce15..c0ca35d 100644
--- a/cmake/README_CMAKE.txt
+++ b/cmake/README_CMAKE.txt
@@ -45,9 +45,8 @@ Limitations include:
   * Not all options available in `./configure` have been ported to
     this CMake system.
 
-  * Password management is included for OS X, but not
-    tested. Gnome-keyring and libsecret support has not
-    been ported over.
+  * Password management is included for OS X and libsecret, but not
+    tested. Gnome-keyring support has not been ported over.
 
   * The Xcode build only supports the Debug configuration. Others such
     as Release are not supported yet.
diff --git a/gnucash/gnome-utils/CMakeLists.txt b/gnucash/gnome-utils/CMakeLists.txt
index f772cb0..4f1bac7 100644
--- a/gnucash/gnome-utils/CMakeLists.txt
+++ b/gnucash/gnome-utils/CMakeLists.txt
@@ -212,7 +212,7 @@ add_library (gncmod-gnome-utils
 )
 
 target_link_libraries(gncmod-gnome-utils gncmod-app-utils gncmod-engine gnc-backend-xml-utils
-     ${CMAKE_DL_LIBS} ${GTK3_LDFLAGS} ${GTK_MAC_LDFLAGS})
+     ${CMAKE_DL_LIBS} ${GTK3_LDFLAGS} ${LIBSECRET_LDFLAGS} ${GTK_MAC_LDFLAGS})
 
 target_compile_definitions(gncmod-gnome-utils PUBLIC ${GTK_MAC_CFLAGS_OTHER}
   PRIVATE -DG_LOG_DOMAIN=\"gnc.gui\")
@@ -232,6 +232,7 @@ target_include_directories(gncmod-gnome-utils
       ${CMAKE_CURRENT_SOURCE_DIR}
     PRIVATE
       ${GTK_MAC_INCLUDE_DIRS}
+      ${LIBSECRET_INCLUDE_DIRS}
       ${CMAKE_CURRENT_BINARY_DIR}
 )
 

commit 2e4e18e21e60ffea64face767694c9e62cd625bf
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Wed Jun 13 14:46:56 2018 +0800

    [test-net-charts] initial commit test-net-charts
    
    This just tests that the report exists. It does not do any checking
    for the data generated yet.

diff --git a/gnucash/report/standard-reports/test/CMakeLists.txt b/gnucash/report/standard-reports/test/CMakeLists.txt
index f994b17..9646dfa 100644
--- a/gnucash/report/standard-reports/test/CMakeLists.txt
+++ b/gnucash/report/standard-reports/test/CMakeLists.txt
@@ -7,6 +7,7 @@ set(scm_test_standard_reports_SOURCES
 )
 
 set(scm_test_with_srfi64_SOURCES
+  test-net-charts.scm
   test-transaction.scm
   test-income-gst.scm
 )
diff --git a/gnucash/report/standard-reports/test/test-net-charts.scm b/gnucash/report/standard-reports/test/test-net-charts.scm
new file mode 100644
index 0000000..32e7578
--- /dev/null
+++ b/gnucash/report/standard-reports/test/test-net-charts.scm
@@ -0,0 +1,101 @@
+(use-modules (gnucash gnc-module))
+(gnc:module-begin-syntax (gnc:module-load "gnucash/app-utils" 0))
+(use-modules (gnucash engine test test-extras))
+(use-modules (gnucash report standard-reports net-barchart))
+(use-modules (gnucash report standard-reports net-linechart))
+(use-modules (gnucash report stylesheets))
+(use-modules (gnucash report report-system))
+(use-modules (gnucash report report-system test test-extras))
+(use-modules (srfi srfi-64))
+(use-modules (gnucash engine test srfi64-extras))
+(use-modules (sxml simple))
+(use-modules (sxml xpath))
+(use-modules (system vm coverage))
+(use-modules (system vm vm))
+
+(define variant-alist
+  (list
+   (cons 'net-worth-barchart "cbba1696c8c24744848062c7f1cf4a72")
+   (cons 'net-worth-linechart "d8b63264186b11e19038001558291366")
+   (cons 'income-expense-barchart "80769921e87943adade887b9835a7685")
+   (cons 'income-expense-linechart "e533c998186b11e1b2e2001558291366")))
+
+(define (variant->uuid variant)
+  (cdr (assq variant variant-alist)))
+
+;; Explicitly set locale to make the report output predictable
+(setlocale LC_ALL "C")
+
+(define (run-test)
+  (test-runner-factory gnc:test-runner)
+  (test-begin "net-charts.scm")
+  (for-each (lambda (variant)
+              (null-test variant))
+            (map car variant-alist))
+  (for-each (lambda (variant)
+              (net-charts-test variant))
+            (map car variant-alist))
+  (test-end "net-charts.scm"))
+
+(define (options->render variant options test-title)
+  ;; options object -> string
+  ;; It also dumps the render into /tmp/test-net-charts-XX.html where XX is the test title
+  (gnc:options->render variant options "test-net-charts-~a" test-title))
+
+(define structure
+  (list "Root" (list (cons 'type ACCT-TYPE-ASSET))
+        (list "Asset"
+              (list "Bank"))
+        (list "Income" (list (cons 'type ACCT-TYPE-INCOME)))
+        (list "Expenses" (list (cons 'type ACCT-TYPE-EXPENSE)))
+        (list "Equity" (list (cons 'type ACCT-TYPE-EQUITY)))))
+
+(define (set-option! options section name value)
+  (let ((option (gnc:lookup-option options section name)))
+    (if option
+        (gnc:option-set-value option value)
+        (test-assert (format #f "wrong-option ~a ~a" section name) #f))))
+
+(define (null-test variant)
+  ;; This null-test tests for the presence of report.
+  (let* ((uuid (variant->uuid variant))
+         (options (gnc:make-report-options uuid)))
+    (test-assert (format #f "null-test: ~a" variant)
+      (options->render uuid options "null-test"))))
+
+
+;; the following tests are not ready yet
+;; unfortunately sxml parsing requires a very valid xhtml, which means
+;; <script>
+
+(define (net-charts-test variant)
+  (let* ((uuid (variant->uuid variant))
+         (env (create-test-env))
+         (account-alist (env-create-account-structure-alist env structure))
+         (bank (cdr (assoc "Bank" account-alist)))
+         (income (cdr (assoc "Income" account-alist)))
+         (expense (cdr (assoc "Expenses" account-alist)))
+         (equity (cdr (assoc "Equity" account-alist)))
+         (YEAR (gnc:time64-get-year (gnc:get-today))))
+
+    (define (default-testing-options)
+      (let ((options (gnc:make-report-options (variant->uuid variant))))
+        (set-option! options "Accounts" "Accounts" (list bank))
+        (set-option! options "General" "Start Date" (cons 'relative 'start-cal-year))
+        (set-option! options "General" "End Date" (cons 'relative 'end-cal-year))
+        options))
+
+    (env-transfer env 01 01 YEAR bank expense       5   #:description "desc-1" #:num "trn1" #:memo "memo-3")
+    (env-transfer env 21 02 YEAR income bank       10   #:description "desc-2" #:num "trn2" #:void-reason "void" #:notes "notes3")
+    (env-transfer env 11 02 YEAR income bank       29   #:description "desc-3" #:num "trn3"
+                  #:reconcile (cons #\c (gnc-dmy2time64 01 03 YEAR)))
+    (env-transfer env 01 02 YEAR bank expense      15   #:description "desc-4" #:num "trn4" #:notes "notes2" #:memo "memo-1")
+    (env-transfer env 10 03 YEAR bank expense      10   #:description "desc-5" #:num "trn5" #:void-reason "any")
+    (env-transfer env 10 03 YEAR expense bank      11   #:description "desc-6" #:num "trn6" #:notes "notes1")
+    (env-transfer env 10 04 YEAR income bank        8   #:description "desc-7" #:num "trn7" #:notes "notes1" #:memo "memo-2"
+                  #:reconcile (cons #\y (gnc-dmy2time64 01 03 YEAR)))
+
+    (let* ((options (default-testing-options)))
+      (test-assert (format #f "basic report exists: ~a" variant)
+        (options->render uuid options (format #f "net-charts-test ~a default options" variant))))))
+

commit 7de68cef887f56a5676da433217a7d88dc1c2646
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Wed Jun 13 20:29:27 2018 +0800

    [test-extras] split gnc:options->sxml into 2 functions
    
    Creates (gnc:options->render) which outputs report as a string.

diff --git a/gnucash/report/report-system/test/test-extras.scm b/gnucash/report/report-system/test/test-extras.scm
index bdbd92b..e6e02bf 100644
--- a/gnucash/report/report-system/test/test-extras.scm
+++ b/gnucash/report/report-system/test/test-extras.scm
@@ -32,8 +32,6 @@
 (export tbl-ref)
 (export tbl-ref->number)
 
-(export gnc:options->sxml)
-
 ;;
 ;; Table parsing
 ;;
@@ -85,15 +83,17 @@
 (define (tbl-ref->number tbl row-index column-index)
   (string->number (car (tbl-ref tbl row-index column-index))))
 
-
-(define (gnc:options->sxml uuid options prefix test-title)
+(export gnc:options->render)
+(define (gnc:options->render uuid options prefix test-title)
   ;; uuid - str to locate report uuid
-  ;; options object -> sxml tree
+  ;; options - gnc:options object
   ;; prefix - str describing tests e.g. "test-trep"
   ;; test-title: str describing each unit test e.g. "test disable filter"
   ;;
-  ;; This function abstracts the report renderer. It also catches XML
-  ;; parsing errors, dumping the options changed.
+  ;; outputs: string
+  ;;
+  ;; This function abstracts the report renderer, producing a string. It
+  ;; can be useful for reports which may not valid XML.
   ;;
   ;; It also dumps the render into /tmp/XX-YY.html where XX is the
   ;; test prefix and YY is the test title.
@@ -105,25 +105,33 @@
          (document (renderer report))
          (sanitize-char (lambda (c)
                           (if (or (char-alphabetic? c)
-                                  (char-numeric? c)) c #\-)))
-         (fileprefix (string-map sanitize-char prefix))
-         (filename (string-map sanitize-char test-title)))
+                                  (char-numeric? c)) c #\-))))
     (gnc:html-document-set-style-sheet! document (gnc:report-stylesheet report))
     (if test-title
         (gnc:html-document-set-title! document test-title))
-    (let* ((filename (format #f "/tmp/~a-~a.html" fileprefix filename))
-           (render (gnc:html-document-render document)))
-      (with-output-to-file filename
+    (let ((render (gnc:html-document-render document)))
+      (with-output-to-file (format #f "/tmp/~a-~a.html"
+                                   (string-map sanitize-char prefix)
+                                   (string-map sanitize-char test-title))
         (lambda ()
           (display render)))
-      (catch 'parser-error
-        (lambda () (xml->sxml render
-                              #:trim-whitespace? #t
-                              #:entities '((nbsp . "\xa0"))))
-        (lambda (k . args)
-          (format #t "*** XML error. see render output at ~a\n~a"
-                  filename (gnc:html-render-options-changed options #t))
-          (throw k args))))))
+      render)))
+
+(export gnc:options->sxml)
+(define (gnc:options->sxml uuid options prefix test-title)
+  ;; This functions calls the above gnc:options->render to render
+  ;; report.  Then report is converted to SXML.  It catches XML
+  ;; parsing errors, dumping the options changed.
+  (let ((render (gnc:options->render uuid options prefix test-title)))
+    (catch 'parser-error
+      (lambda () (xml->sxml render
+                            #:trim-whitespace? #t
+                            #:entities '((nbsp . "\xa0"))))
+      (lambda (k . args)
+        (format #t "*** XML error: ~a ~a\n~a"
+                prefix test-title
+                (gnc:html-render-options-changed options #t))
+        (throw k args)))))
 
 (export sxml->table-row-col)
 (define (sxml->table-row-col sxml tbl row col)

commit 1fc5634c7acbfe16aec273c30a54f268ec7ad4a0
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Jun 10 10:37:43 2018 +0800

    [html-text] [bugfix] img url was mistakenly disabled.

diff --git a/gnucash/report/report-system/html-text.scm b/gnucash/report/report-system/html-text.scm
index b68ed2c..bd586be 100644
--- a/gnucash/report/report-system/html-text.scm
+++ b/gnucash/report/report-system/html-text.scm
@@ -200,7 +200,7 @@
      (lambda ()
        (for-each 
         (lambda (kvp)
-          (format #f "~a=~s " (car kvp) (cadr kvp)))
+          (format #t "~a=~s " (car kvp) (cadr kvp)))
         (cons (list 'src src)
               rest))))))
 

commit 6c03d07e00bc6ff423a41b799e9ac904a9ea97b8
Merge: 87f4791 e4407de
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Jun 12 16:28:28 2018 -0700

    Merge Chris Lam's 'maint-test-invoice' into maint.


commit 87f4791fae07d9d961363891caa0842fd58bd036
Merge: 9c7fa77 af00712
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Jun 12 16:21:26 2018 -0700

    Merge Chris Lam's 'maint-fix-796537' into maint.


commit 9c7fa77d3b1d161e9319d8b392bb89148318e8b0
Merge: 5d3ae6c 6483782
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Jun 12 16:12:48 2018 -0700

    Merge Bob Fewell's 'fixes6' into maint.


commit 5d3ae6c672e95327b8b7476a800e5e9cb6afbb28
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Jun 12 16:12:30 2018 -0700

    Fix test-tokenizer failure due to not nulling the GError*.

diff --git a/gnucash/import-export/csv-imp/gnc-tokenizer.cpp b/gnucash/import-export/csv-imp/gnc-tokenizer.cpp
index f8917f7..0a15fa0 100644
--- a/gnucash/import-export/csv-imp/gnc-tokenizer.cpp
+++ b/gnucash/import-export/csv-imp/gnc-tokenizer.cpp
@@ -47,7 +47,7 @@ GncTokenizer::load_file(const std::string& path)
     m_imp_file_str = path;
     char *raw_contents;
     size_t raw_length;
-    GError *error;
+    GError *error = nullptr;
 
     if (!g_file_get_contents(path.c_str(), &raw_contents, &raw_length, &error))
       throw std::ifstream::failure(error->message);

commit ffe6044cd66edf39ae22875ef475d8e655b93526
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Jun 12 15:49:28 2018 -0700

    Bug 796248 - Editing Scheduled Transaction
    
    An extra XaccTransBeginEdit, never committed, for transactions that
    the backend tried to load when they were already there. That made
    the register think that something else had it open.

diff --git a/libgnucash/backend/sql/gnc-transaction-sql.cpp b/libgnucash/backend/sql/gnc-transaction-sql.cpp
index f2e2b1f..c62f3f1 100644
--- a/libgnucash/backend/sql/gnc-transaction-sql.cpp
+++ b/libgnucash/backend/sql/gnc-transaction-sql.cpp
@@ -292,12 +292,12 @@ load_single_tx (GncSqlBackend* sql_be, GncSqlRow& row)
     if (guid == NULL) return NULL;
     tx_guid = *guid;
 
-    // Don't overwrite the transaction if it's already been loaded (and possibly modified).
-    // However increase the edit level, it may be modified while loading its splits
+    /* Don't overwrite the transaction if it's already been loaded (and possibly
+     * modified).
+     */
     pTx = xaccTransLookup (&tx_guid, sql_be->book());
     if (pTx != NULL)
     {
-        xaccTransBeginEdit (pTx);
         return NULL;
     }
 

commit c8861d4666ff884812d2be1c786bde621e970b16
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Jun 12 14:53:27 2018 -0700

    Bug 795276 - Invalid date on price stops file from being parsed.
    
    Instead of reporting an error and declining to load the file (XML)
    or failing to enter a value (SQL) when a bad date is found in the
    database, use a 0 time stamp (1970-01-01 00:00:00 UTC). Adds a warning
    in SQL backends; there was one already in XML.

diff --git a/libgnucash/backend/sql/gnc-sql-column-table-entry.cpp b/libgnucash/backend/sql/gnc-sql-column-table-entry.cpp
index cba8d17..63a4f7a 100644
--- a/libgnucash/backend/sql/gnc-sql-column-table-entry.cpp
+++ b/libgnucash/backend/sql/gnc-sql-column-table-entry.cpp
@@ -401,7 +401,9 @@ GncSqlColumnTableEntryImpl<CT_TIMESPEC>::load (const GncSqlBackend* sql_be,
         }
         catch (std::invalid_argument&)
         {
-            return;
+            PWARN("An invalid date was found in your database."
+                  "It has been set to 1 January 1970.");
+            ts.tv_sec = 0;
         }
     }
     set_parameter(pObject, &ts,
diff --git a/libgnucash/backend/xml/gnc-entry-xml-v2.cpp b/libgnucash/backend/xml/gnc-entry-xml-v2.cpp
index f87be3f..a25fb56 100644
--- a/libgnucash/backend/xml/gnc-entry-xml-v2.cpp
+++ b/libgnucash/backend/xml/gnc-entry-xml-v2.cpp
@@ -244,7 +244,7 @@ set_time64 (xmlNodePtr node, GncEntry* entry,
               void (*func) (GncEntry* entry, time64 ts))
 {
     time64 time = dom_tree_to_time64 (node);
-    if (!dom_tree_valid_time64 (time, node->name)) return FALSE;
+    if (!dom_tree_valid_time64 (time, node->name)) time = 0;
     func (entry, time);
     return TRUE;
 }
diff --git a/libgnucash/backend/xml/gnc-invoice-xml-v2.cpp b/libgnucash/backend/xml/gnc-invoice-xml-v2.cpp
index 1d3aef2..b2f45f5 100644
--- a/libgnucash/backend/xml/gnc-invoice-xml-v2.cpp
+++ b/libgnucash/backend/xml/gnc-invoice-xml-v2.cpp
@@ -188,7 +188,7 @@ set_time64 (xmlNodePtr node, GncInvoice* invoice,
               void (*func) (GncInvoice* invoice, time64 time))
 {
     time64 time = dom_tree_to_time64 (node);
-    if (!dom_tree_valid_time64 (time, node->name)) return FALSE;
+    if (!dom_tree_valid_time64 (time, node->name)) time = 0;
     func (invoice, time);
     return TRUE;
 }
diff --git a/libgnucash/backend/xml/gnc-order-xml-v2.cpp b/libgnucash/backend/xml/gnc-order-xml-v2.cpp
index bb27192..a101d15 100644
--- a/libgnucash/backend/xml/gnc-order-xml-v2.cpp
+++ b/libgnucash/backend/xml/gnc-order-xml-v2.cpp
@@ -135,7 +135,7 @@ set_time64 (xmlNodePtr node, GncOrder* order,
               void (*func) (GncOrder* order, time64 tt))
 {
     time64 time = dom_tree_to_time64 (node);
-    if (!dom_tree_valid_time64 (time, node->name)) return FALSE;
+    if (!dom_tree_valid_time64 (time, node->name)) time = 0;
     func (order, time);
     return TRUE;
 }
diff --git a/libgnucash/backend/xml/gnc-pricedb-xml-v2.cpp b/libgnucash/backend/xml/gnc-pricedb-xml-v2.cpp
index 6bb7dd2..fbde09a 100644
--- a/libgnucash/backend/xml/gnc-pricedb-xml-v2.cpp
+++ b/libgnucash/backend/xml/gnc-pricedb-xml-v2.cpp
@@ -114,7 +114,7 @@ price_parse_xml_sub_node (GNCPrice* p, xmlNodePtr sub_node, QofBook* book)
     else if (g_strcmp0 ("price:time", (char*)sub_node->name) == 0)
     {
         time64 time = dom_tree_to_time64 (sub_node);
-        if (!dom_tree_valid_time64 (time, sub_node->name)) return FALSE;
+        if (!dom_tree_valid_time64 (time, sub_node->name)) time = 0;
         Timespec ts {time, 0};
         gnc_price_set_time (p, ts);
     }
diff --git a/libgnucash/backend/xml/gnc-transaction-xml-v2.cpp b/libgnucash/backend/xml/gnc-transaction-xml-v2.cpp
index ba19590..6a397f6 100644
--- a/libgnucash/backend/xml/gnc-transaction-xml-v2.cpp
+++ b/libgnucash/backend/xml/gnc-transaction-xml-v2.cpp
@@ -283,7 +283,7 @@ spl_reconcile_date_handler (xmlNodePtr node, gpointer data)
 {
     struct split_pdata* pdata = static_cast<decltype (pdata)> (data);
     time64 time  = dom_tree_to_time64 (node);
-    if (!dom_tree_valid_time64 (time, node->name)) return FALSE;
+    if (!dom_tree_valid_time64 (time, node->name)) time = 0;
     xaccSplitSetDateReconciledSecs (pdata->split, time);
     return TRUE;
 }
@@ -438,7 +438,7 @@ set_tran_time64 (xmlNodePtr node, Transaction * trn,
         void (*func) (Transaction *, time64))
 {
     time64 time = dom_tree_to_time64 (node);
-    if (!dom_tree_valid_time64 (time, node->name)) return FALSE;
+    if (!dom_tree_valid_time64 (time, node->name)) time = 0;
     func (trn, time);
     return TRUE;
 }
@@ -448,7 +448,7 @@ set_tran_date (xmlNodePtr node, Transaction* trn,
                void (*func) (Transaction* trn, const Timespec* tm))
 {
     time64 time = dom_tree_to_time64 (node);
-    if (!dom_tree_valid_time64 (time, node->name)) return FALSE;
+    if (!dom_tree_valid_time64 (time, node->name)) time = 0;
     Timespec ts {time, 0};
     func (trn, &ts);
     return TRUE;
diff --git a/libgnucash/backend/xml/io-gncxml-v1.cpp b/libgnucash/backend/xml/io-gncxml-v1.cpp
index b9153be..c25b5cd 100644
--- a/libgnucash/backend/xml/io-gncxml-v1.cpp
+++ b/libgnucash/backend/xml/io-gncxml-v1.cpp
@@ -2960,7 +2960,7 @@ price_parse_xml_sub_node (GNCPrice* p, xmlNodePtr sub_node, QofBook* book)
     else if (g_strcmp0 ("price:time", (char*)sub_node->name) == 0)
     {
         time64 time = dom_tree_to_time64 (sub_node);
-        if (!dom_tree_valid_time64 (time, sub_node->name)) return FALSE;
+        if (!dom_tree_valid_time64 (time, sub_node->name)) time = 0;
         Timespec ts = {time, 0};
         gnc_price_set_time (p, ts);
     }
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 85fb0c0..426b3f2 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -575,6 +575,7 @@ libgnucash/backend/sql/gnc-sql-result.cpp
 libgnucash/backend/sql/gnc-tax-table-sql.cpp
 libgnucash/backend/sql/gnc-transaction-sql.cpp
 libgnucash/backend/sql/gnc-vendor-sql.cpp
+libgnucash/backend/xml/.#gnc-invoice-xml-v2.cpp
 libgnucash/backend/xml/gnc-account-xml-v2.cpp
 libgnucash/backend/xml/gnc-address-xml-v2.cpp
 libgnucash/backend/xml/gnc-backend-xml.cpp

commit 9179505208768fb718b85c50b29b06bf732547f3
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Jun 12 11:43:25 2018 -0700

    Bug 796484 - csv import: iostream error
    
    Unfortunately it turns out that we can't use filestreams because
    they can't take path arguments containing Unicode on Windows.
    Replace the filestream code with g_file_get_contents(),  which
    takes care of all of that Windows compatibility noise for us.

diff --git a/gnucash/import-export/csv-imp/gnc-tokenizer.cpp b/gnucash/import-export/csv-imp/gnc-tokenizer.cpp
index 7b14a2d..f8917f7 100644
--- a/gnucash/import-export/csv-imp/gnc-tokenizer.cpp
+++ b/gnucash/import-export/csv-imp/gnc-tokenizer.cpp
@@ -15,6 +15,8 @@
 
 extern "C" {
 #include <go-glib-extras.h>
+#include <glib.h>
+#include <glib/gstdio.h>
 }
 
 std::unique_ptr<GncTokenizer> gnc_tokenizer_factory(GncImpFileFormat fmt)
@@ -43,18 +45,16 @@ GncTokenizer::load_file(const std::string& path)
         return;
 
     m_imp_file_str = path;
+    char *raw_contents;
+    size_t raw_length;
+    GError *error;
 
-    std::ifstream in;
-    in.exceptions ( std::ifstream::failbit | std::ifstream::badbit );
-    in.open (m_imp_file_str.c_str(), std::ios::in | std::ios::binary);
-
-    m_raw_contents.clear();
-    in.seekg(0, std::ios::end);
-    m_raw_contents.resize(in.tellg());
-    in.seekg(0, std::ios::beg);
-    in.read(&m_raw_contents[0], m_raw_contents.size());
-    in.close();
+    if (!g_file_get_contents(path.c_str(), &raw_contents, &raw_length, &error))
+      throw std::ifstream::failure(error->message);
 
+    m_raw_contents = raw_contents;
+    g_free(raw_contents);
+    
     // Guess encoding, user can override if needed later on.
     const char *guessed_enc = NULL;
     guessed_enc = go_guess_encoding (m_raw_contents.c_str(),

commit e4407dee9b4f0bfe8a75e7415c7ddeb60a98a60f
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sat Jun 2 22:51:29 2018 +0800

    [test-invoice] add gncOrder display and testing

diff --git a/gnucash/report/business-reports/test/test-invoice.scm b/gnucash/report/business-reports/test/test-invoice.scm
index 8c4fa0e..3db7c4a 100644
--- a/gnucash/report/business-reports/test/test-invoice.scm
+++ b/gnucash/report/business-reports/test/test-invoice.scm
@@ -479,6 +479,12 @@
                                    (gncTaxTableEntrySetAmount entry taxrate)
                                    (gncTaxTableAddEntry tt entry))
                                  tt))
+           (order (let ((order (gncOrderCreate (gnc-get-current-book))))
+                    (gncOrderSetID order "order-id")
+                    (gncOrderSetOwner order owner-1)
+                    (gncOrderSetReference order "order-ref")
+                    (gncOrderSetActive order #t)
+                    order))
            (billterm (let ((term (gncBillTermCreate (gnc-get-current-book))))
                        (gncBillTermSetName term "billterm-name")
                        (gncBillTermSetDescription term "billterm-desc")
@@ -525,6 +531,7 @@
            ;; (format #t "inv-8: adding ~a to invoice, entry amount is ~a\n"
            ;;         desc
            ;;         (exact->inexact (gncEntryGetDocValue each-entry #f #t #f)))
+           (gncOrderAddEntry order each-entry)
            (gncInvoiceAddEntry inv-8 each-entry)))
        (list
         ;; the following list specifies combinations to test gncEntry options
@@ -552,6 +559,10 @@
           (member
            "Terms:\xa0billterm-desc"
            ((sxpath '(// body // *text*)) sxml)))
+        (test-assert "inv-8 gncOrder reference is in invoice body"
+          (member
+           "REF:\xa0order-ref"
+           ((sxpath '(// body // *text*)) sxml)))
         (case variant
           ((invoice)
            (test-equal "inv-8 invoice date is in invoice body"

commit aa4dfb0ead04a0eb4b55e35becc9d429443336cc
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun May 27 20:48:32 2018 +0800

    [test-invoice] also test fancy-invoice

diff --git a/gnucash/report/business-reports/test/test-invoice.scm b/gnucash/report/business-reports/test/test-invoice.scm
index 1c30a18..8c4fa0e 100644
--- a/gnucash/report/business-reports/test/test-invoice.scm
+++ b/gnucash/report/business-reports/test/test-invoice.scm
@@ -40,7 +40,7 @@
   (test-begin "test-invoice.scm")
   (inv-tests 'invoice)
   (inv-tests 'easy-invoice)
-  ;; (inv-tests 'fancy-invoice)
+  (inv-tests 'fancy-invoice)
   (test-end "test-invoice.scm"))
 
 (define (sxml-main-get-row-col sxml row col)
@@ -234,16 +234,16 @@
       (test-equal "inv-1 simple entry details are correct"
         '("entry-1-desc" "entry-1-action" "2.00" "$3.00" "0.00 %" "T" "$0.00" "$6.00")
         (cdr (sxml-main-get-row-col sxml 1 #f)))
-      (test-equal "inv-1 cust-name is correct"
-        '("cust-1-name")
-        ((sxpath '(// (table 2) // tbody // tr // td // *text*))
-         sxml))
+      (unless (eq? variant 'fancy-invoice)
+        (test-equal "inv-1 cust-name is correct"
+          '("cust-1-name")
+          ((sxpath '(// (table 2) // tbody // tr // td // *text*))
+           sxml)))
       (test-assert "inv-1-billing-id is in invoice body"
         (member
          (case variant
-           ((invoice) "Reference:\xa0inv-1-billing-id")
-           ((easy-invoice) "Billing ID:\xa0inv-1-billing-id")
-           (else ""))
+           ((invoice fancy-invoice) "Reference:\xa0inv-1-billing-id")
+           ((easy-invoice) "Billing ID:\xa0inv-1-billing-id"))
          ((sxpath '(// body // *text*)) sxml)))
       (test-assert "inv-1 inv-notes is in invoice body"
         (member
@@ -257,12 +257,13 @@
       (test-equal "inv-1 sparse simple entry headers are correct"
         (case variant
           ((invoice) '("Net Price" "Tax" "Total Price" "Amount Due"))
-          (else '("Tax" "Total Price" "Amount Due")))
+          ((fancy-invoice) '("Net Price" "Tax" "Total\xa0Price" "Amount\xa0Due"))
+          ((easy-invoice) '("Tax" "Total Price" "Amount Due")))
         (sxml-main-get-row-col sxml #f 1))
       (test-equal "inv-1 sparse simple entry amounts are correct"
         (case variant
-          ((invoice) '("$6.00" "$0.00" "$6.00" "$6.00"))
-          (else '("$0.00" "$6.00" "$6.00")))
+          ((invoice fancy-invoice) '("$6.00" "$0.00" "$6.00" "$6.00"))
+          ((easy-invoice) '("$0.00" "$6.00" "$6.00")))
         (sxml-main-get-row-col sxml #f -1)))
     (test-end "inv-1 simple entry, sparse options")
 
@@ -296,10 +297,11 @@
       (test-equal "inv-2 simple entry details are correct"
         '("entry-inv-2-desc" "entry-inv-2-action" "2.00" "$3.00" "0.00 %" "T" "$0.00" "$6.00")
         (cdr (sxml-main-get-row-col sxml 1 #f)))
-      (test-equal "inv-2 cust-name is correct"
-        '("cust-1-name")
-        ((sxpath '(// (table 2) // tbody // tr // td // *text*))
-         sxml))
+      (unless (eq? variant 'fancy-invoice)
+        (test-equal "inv-2 cust-name is correct"
+          '("cust-1-name")
+          ((sxpath '(// (table 2) // tbody // tr // td // *text*))
+           sxml)))
       (test-assert "inv-2 inv-notes is in invoice body"
         (member
          "inv-2-notes"
@@ -335,10 +337,11 @@
       (test-equal "inv-3 simple entry details are correct"
         '("entry-inv-3-desc" "entry-inv-3-action" "2.00" "$3.00" "T" "$0.00" "$6.00")
         (cdr (sxml-main-get-row-col sxml 1 #f)))
-      (test-equal "inv-3 vend-name is correct"
-        '("vend-1-name")
-        ((sxpath '(// (table 2) // tbody // tr // td // *text*))
-         sxml))
+      (unless (eq? variant 'fancy-invoice)
+        (test-equal "inv-3 vend-name is correct"
+          '("vend-1-name")
+          ((sxpath '(// (table 2) // tbody // tr // td // *text*))
+           sxml)))
       (test-assert "inv-3 inv-notes is in invoice body"
         (member
          "inv-3-notes"
@@ -365,10 +368,11 @@
       (test-equal "inv-4 simple entry details are correct"
         '("entry-inv-4-desc" "entry-inv-4-action" "2.00" "$3.00" "T" "$0.00" "$6.00")
         (cdr (sxml->table-row-col sxml 3 1 #f)))
-      (test-equal "inv-4 vend-name is correct"
-        '("emp-1-name" "emp-1-name")    ;FIXME: why is this duplicated????
-        ((sxpath '(// (table 2) // tbody // tr // td // *text*))
-         sxml))
+      (unless (eq? variant 'fancy-invoice)
+        (test-equal "inv-4 vend-name is correct"
+          '("emp-1-name" "emp-1-name")    ;FIXME: why is this duplicated????
+          ((sxpath '(// (table 2) // tbody // tr // td // *text*))
+           sxml)))
       (test-assert "inv-4 inv-notes is in invoice body"
         (member
          "inv-4-notes"
@@ -394,10 +398,11 @@
       (test-equal "inv-5 simple entry details are correct"
         '("entry-5-desc" "entry-5-action" "2.00" "$3.00" "0.00 %" "T" "$0.00" "$6.00")
         (cdr (sxml-main-get-row-col sxml 1 #f)))
-      (test-equal "inv-5 cust-name is correct"
-        '("cust-1-name")
-        ((sxpath '(// (table 2) // tbody // tr // td // *text*))
-         sxml)))
+      (unless (eq? variant 'fancy-invoice)
+        (test-equal "inv-5 cust-name is correct"
+          '("cust-1-name")
+          ((sxpath '(// (table 2) // tbody // tr // td // *text*))
+           sxml))))
     (test-end "inv-5 simple entry")
 
     (test-begin "inv-6")
@@ -418,10 +423,11 @@
       (test-equal "inv-6 simple entry details are correct"
         '("entry-inv-6-desc" "entry-inv-6-action" "2.00" "$3.00" "T" "$0.00" "$6.00")
         (cdr (sxml-main-get-row-col sxml 1 #f)))
-      (test-equal "inv-6 vend-name is correct"
-        '("vend-1-name")
-        ((sxpath '(// (table 2) // tbody // tr // td // *text*))
-         sxml))
+      (unless (eq? variant 'fancy-invoice)
+        (test-equal "inv-6 vend-name is correct"
+          '("vend-1-name")
+          ((sxpath '(// (table 2) // tbody // tr // td // *text*))
+           sxml)))
       (test-assert "inv-6 inv-3-notes is in invoice body"
         (member
          "inv-3-notes"
@@ -447,10 +453,11 @@
       (test-equal "inv-7 simple entry details are correct"
         '("entry-inv-7-desc" "entry-inv-7-action" "2.00" "$3.00" "T" "$0.00" "$6.00")
         (cdr (sxml-main-get-row-col sxml 1 #f)))
-      (test-equal "inv-7 vend-name is correct"
-        '("emp-1-name" "emp-1-name")    ;FIXME: why is this duplicated????
-        ((sxpath '(// (table 2) // tbody // tr // td // *text*))
-         sxml))
+      (unless (eq? variant 'fancy-invoice)
+        (test-equal "inv-7 vend-name is correct"
+          '("emp-1-name" "emp-1-name")    ;FIXME: why is this duplicated????
+          ((sxpath '(// (table 2) // tbody // tr // td // *text*))
+           sxml)))
       (test-assert "inv-7 inv-4-notes is in invoice body"
         (member
          "inv-4-notes"
@@ -546,14 +553,14 @@
            "Terms:\xa0billterm-desc"
            ((sxpath '(// body // *text*)) sxml)))
         (case variant
-          ((invoice fancy-invoice)
+          ((invoice)
            (test-equal "inv-8 invoice date is in invoice body"
              '("Invoice Date:\xa0")
              (sxml->table-row-col sxml 2 1 1))
            (test-equal "inv-8 due date is in invoice body"
              '("Due Date:\xa0")
              (sxml->table-row-col sxml 2 2 1)))
-          (else
+          ((easy-invoice)
            (test-equal "inv-8 invoice date is in invoice body"
              '("Date:\xa0")
              (sxml->table-row-col sxml 3 1 1))
@@ -564,7 +571,9 @@
           '("$2,133.25" "$2,061.96" "$2,133.25" "$2,061.96" "$2,133.25" "$2,133.25"
             "$1,851.95" "$1,859.30" "$16,368.17" "$1,111.01" "$17,479.18"
             "-$17,479.18" "$0.00")
-          (sxml->table-row-col sxml 4 #f -1))
+          (if (eq? variant 'fancy-invoice)
+              (sxml->table-row-col sxml 3 #f -1)
+              (sxml->table-row-col sxml 4 #f -1)))
         (test-assert "inv-8 is fully paid up!"
           (gncInvoiceIsPaid inv-8))))
     (test-end "combinations of gncEntry options")))

commit bb37adc3ec86b803e56edc4072b2e1a019851c12
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun May 27 18:27:42 2018 +0800

    [test-invoice] also test easy-invoice

diff --git a/gnucash/report/business-reports/test/test-invoice.scm b/gnucash/report/business-reports/test/test-invoice.scm
index b60b031..1c30a18 100644
--- a/gnucash/report/business-reports/test/test-invoice.scm
+++ b/gnucash/report/business-reports/test/test-invoice.scm
@@ -39,7 +39,7 @@
   (test-runner-factory gnc:test-runner)
   (test-begin "test-invoice.scm")
   (inv-tests 'invoice)
-  ;; (inv-tests 'easy-invoice)
+  (inv-tests 'easy-invoice)
   ;; (inv-tests 'fancy-invoice)
   (test-end "test-invoice.scm"))
 
@@ -184,19 +184,34 @@
                                         (gncTaxTableAddEntry tt entry))
                                       tt)))
 
-    (define (default-testing-options inv)
+    (define* (default-testing-options inv #:optional (setting #t))
       (let ((options (gnc:make-report-options uuid)))
         (set-option! options "General" "Invoice Number" inv)
         (for-each
          (lambda (disp-col-name)
-           (set-option! options "Display Columns" disp-col-name #t))
-         '("Date" "Description" "Action" "Quantity" "Price" "Discount"
-           "Taxable" "Tax Amount" "Total"))
+           (set-option! options "Display Columns" disp-col-name setting))
+         (case variant
+           ((invoice fancy-invoice)
+            '("Date" "Description" "Action" "Quantity" "Price" "Discount"
+              "Taxable" "Tax Amount" "Total"))
+           ((easy-invoice)
+            '("Date" "Description" "Charge Type" "Quantity"
+              "Price" "Discount" "Taxable" "Tax Amount" "Total"))))
         (for-each
          (lambda (disp-col-name)
-           (set-option! options "Display" disp-col-name #t))
-         '("Individual Taxes" "Totals" "References" "Billing Terms"
-           "Billing ID" "Invoice Notes" "Payments" "Job Details"))
+           (set-option! options "Display" disp-col-name setting))
+         (case variant
+           ((invoice)
+            '("Individual Taxes" "Totals" "References" "Billing Terms"
+              "Billing ID" "Invoice Notes" "Payments" "Job Details"))
+           ((fancy-invoice)
+            '("Individual Taxes" "Totals" "References" "Billing Terms"
+              "Billing ID" "Invoice Notes" "Payments"))
+           ((easy-invoice)
+            '("My Company" "My Company ID" "Due Date"
+              "Individual Taxes" "Totals" "Subtotal" "References"
+              "Billing Terms" "Billing ID" "Invoice Notes"
+              "Payments"))))
         options))
 
     ;; entry-1  2 widgets of $3 = $6
@@ -225,7 +240,10 @@
          sxml))
       (test-assert "inv-1-billing-id is in invoice body"
         (member
-         "Reference:\xa0inv-1-billing-id"
+         (case variant
+           ((invoice) "Reference:\xa0inv-1-billing-id")
+           ((easy-invoice) "Billing ID:\xa0inv-1-billing-id")
+           (else ""))
          ((sxpath '(// body // *text*)) sxml)))
       (test-assert "inv-1 inv-notes is in invoice body"
         (member
@@ -234,24 +252,17 @@
     (test-end "inv-1 simple entry")
 
     (test-begin "inv-1 simple entry, sparse options")
-    (let* ((options (let ((options (default-testing-options inv-1)))
-                      (for-each
-                       (lambda (disp-col-name)
-                         (set-option! options "Display Columns" disp-col-name #f))
-                       '("Date" "Description" "Action" "Quantity" "Price" "Discount"
-                         "Taxable" "Tax Amount" "Total"))
-                      (for-each
-                       (lambda (disp-col-name)
-                         (set-option! options "Display" disp-col-name #f))
-                       '("Individual Taxes" "Totals" "References" "Billing Terms"
-                         "Billing ID" "Invoice Notes" "Payments" "Job Details"))
-                      options))
+    (let* ((options (default-testing-options inv-1 #f))
            (sxml (options->sxml options "inv-1 simple entry sparse")))
       (test-equal "inv-1 sparse simple entry headers are correct"
-        '("Net Price" "Tax" "Total Price" "Amount Due")
+        (case variant
+          ((invoice) '("Net Price" "Tax" "Total Price" "Amount Due"))
+          (else '("Tax" "Total Price" "Amount Due")))
         (sxml-main-get-row-col sxml #f 1))
       (test-equal "inv-1 sparse simple entry amounts are correct"
-        '("$6.00" "$0.00" "$6.00" "$6.00")
+        (case variant
+          ((invoice) '("$6.00" "$0.00" "$6.00" "$6.00"))
+          (else '("$0.00" "$6.00" "$6.00")))
         (sxml-main-get-row-col sxml #f -1)))
     (test-end "inv-1 simple entry, sparse options")
 
@@ -293,14 +304,15 @@
         (member
          "inv-2-notes"
          ((sxpath '(// body // *text*)) sxml)))
-      (test-assert "inv-2 jobnumber is in invoice body"
-        (member
-         "Job number:\xa0job-1-id"
-         ((sxpath '(// body // *text*)) sxml)))
-      (test-assert "inv-2 jobname is in invoice body"
-        (member
-         "Job name:\xa0job-1-name"
-         ((sxpath '(// body // *text*)) sxml)))
+      (when (eq? variant 'invoice)
+        (test-assert "inv-2 jobnumber is in invoice body"
+          (member
+           "Job number:\xa0job-1-id"
+           ((sxpath '(// body // *text*)) sxml)))
+        (test-assert "inv-2 jobname is in invoice body"
+          (member
+           "Job name:\xa0job-1-name"
+           ((sxpath '(// body // *text*)) sxml))))
       )
     (test-end "inv-2")
 
@@ -533,12 +545,21 @@
           (member
            "Terms:\xa0billterm-desc"
            ((sxpath '(// body // *text*)) sxml)))
-        (test-equal "inv-8 invoice date is in invoice body"
-          '("Invoice Date:\xa0")
-          (sxml->table-row-col sxml 2 1 1))
-        (test-equal "inv-8 due date is in invoice body"
-          '("Due Date:\xa0")
-          (sxml->table-row-col sxml 2 2 1))
+        (case variant
+          ((invoice fancy-invoice)
+           (test-equal "inv-8 invoice date is in invoice body"
+             '("Invoice Date:\xa0")
+             (sxml->table-row-col sxml 2 1 1))
+           (test-equal "inv-8 due date is in invoice body"
+             '("Due Date:\xa0")
+             (sxml->table-row-col sxml 2 2 1)))
+          (else
+           (test-equal "inv-8 invoice date is in invoice body"
+             '("Date:\xa0")
+             (sxml->table-row-col sxml 3 1 1))
+           (test-equal "inv-8 invoice date is in invoice body"
+             '("Due:\xa0")
+             (sxml->table-row-col sxml 3 2 1))))
         (test-equal "inv-8 combo amounts are correct"
           '("$2,133.25" "$2,061.96" "$2,133.25" "$2,061.96" "$2,133.25" "$2,133.25"
             "$1,851.95" "$1,859.30" "$16,368.17" "$1,111.01" "$17,479.18"

commit 81303b4193dd9466af69a28cc299d3e672ca354b
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun May 27 17:18:06 2018 +0800

    [test-invoice] last test inv-8 is paid up
    
    Note there is a debugging display which unexpectedly causes test
    failure. Calling gncEntryGetDocValue for each entry, with any
    combination of booleans, would cause the amounts to change slightly.

diff --git a/gnucash/report/business-reports/test/test-invoice.scm b/gnucash/report/business-reports/test/test-invoice.scm
index 2f196b1..b60b031 100644
--- a/gnucash/report/business-reports/test/test-invoice.scm
+++ b/gnucash/report/business-reports/test/test-invoice.scm
@@ -493,6 +493,19 @@
            (gncEntrySetInvTaxable each-entry taxable?)
            (gncEntrySetInvTaxIncluded each-entry tax-included?)
            (gncEntrySetInvTaxTable each-entry combo-vat-sales-tt)
+           ;; FIXME: Note: The following function hides a subtle
+           ;; bug. It aims to retrieve & dump the entry description
+           ;; and amount. Unfortunately the (gncEntryGetDocValue)
+           ;; function will subtly modify the entry amounts by a
+           ;; fraction; this means that the subsequent invoice payment
+           ;; will not make the invoice amount completely zero. If the
+           ;; following statement is uncommented, the invoice
+           ;; generated will not change, however, the test will fail
+           ;; because the (gncInvoiceIsPaid) final test will fail.
+
+           ;; (format #t "inv-8: adding ~a to invoice, entry amount is ~a\n"
+           ;;         desc
+           ;;         (exact->inexact (gncEntryGetDocValue each-entry #f #t #f)))
            (gncInvoiceAddEntry inv-8 each-entry)))
        (list
         ;; the following list specifies combinations to test gncEntry options
@@ -531,6 +544,6 @@
             "$1,851.95" "$1,859.30" "$16,368.17" "$1,111.01" "$17,479.18"
             "-$17,479.18" "$0.00")
           (sxml->table-row-col sxml 4 #f -1))
-        ))
-    (test-end "combinations of gncEntry options")
-    ))
+        (test-assert "inv-8 is fully paid up!"
+          (gncInvoiceIsPaid inv-8))))
+    (test-end "combinations of gncEntry options")))

commit da1d1b9a47939ad08ac645024f448a4820103ad8
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Thu May 10 23:20:57 2018 +0800

    [test-invoice] initial commit

diff --git a/gnucash/report/business-reports/test/CMakeLists.txt b/gnucash/report/business-reports/test/CMakeLists.txt
index 9c62903..26fe436 100644
--- a/gnucash/report/business-reports/test/CMakeLists.txt
+++ b/gnucash/report/business-reports/test/CMakeLists.txt
@@ -1,5 +1,6 @@
 
 set(scm_test_business_reports_with_srfi64_SOURCES
+  test-invoice.scm
 )
 
 set(GUILE_DEPENDS
diff --git a/gnucash/report/business-reports/test/test-invoice.scm b/gnucash/report/business-reports/test/test-invoice.scm
new file mode 100644
index 0000000..2f196b1
--- /dev/null
+++ b/gnucash/report/business-reports/test/test-invoice.scm
@@ -0,0 +1,536 @@
+(use-modules (gnucash gnc-module))
+(gnc:module-begin-syntax (gnc:module-load "gnucash/app-utils" 0))
+(use-modules (gnucash engine test test-extras))
+(use-modules (gnucash report invoice))
+(use-modules (gnucash report stylesheets))
+(use-modules (gnucash report report-system))
+(use-modules (gnucash report report-system test test-extras))
+(use-modules (srfi srfi-1))
+(use-modules (srfi srfi-64))
+(use-modules (gnucash engine test srfi64-extras))
+(use-modules (sxml simple))
+(use-modules (sxml xpath))
+(use-modules (system vm coverage))
+(use-modules (system vm vm))
+
+(define uuid-list
+  (list (cons 'invoice "5123a759ceb9483abf2182d01c140e8d")
+        (cons 'fancy-invoice "3ce293441e894423a2425d7a22dd1ac6")
+        (cons 'easy-invoice "67112f318bef4fc496bdc27d106bbda4")))
+
+(setlocale LC_ALL "C")
+
+(define (run-test)
+  (if #f
+      (coverage-test run-test-proper)
+      (run-test-proper)))
+
+(define (coverage-test tester)
+  (add-to-load-path "/home/chris/sources/gnucash/gnucash/report/business-reports")
+  (call-with-values
+      (lambda()
+        (with-code-coverage tester))
+    (lambda (data result)
+      (let ((port (open-output-file "/tmp/lcov.info")))
+        (coverage-data->lcov data port)
+        (close port)))))
+
+(define (run-test-proper)
+  (test-runner-factory gnc:test-runner)
+  (test-begin "test-invoice.scm")
+  (inv-tests 'invoice)
+  ;; (inv-tests 'easy-invoice)
+  ;; (inv-tests 'fancy-invoice)
+  (test-end "test-invoice.scm"))
+
+(define (sxml-main-get-row-col sxml row col)
+  (sxml->table-row-col sxml 3 row col))
+
+(define (set-option! options section name value)
+  (let ((option (gnc:lookup-option options section name)))
+    (if option
+        (gnc:option-set-value option value)
+        (test-assert (format #f "wrong-option ~a ~a" section name) #f))))
+
+(define structure
+  (list "Root" (list (cons 'type ACCT-TYPE-ASSET)
+                     (cons 'commodity (gnc-default-report-currency)))
+        (list "Asset"
+              (list "Bank"))
+        (list "VAT"
+              (list "VAT-on-Purchases")
+              (list "VAT-on-Sales" (list (cons 'type ACCT-TYPE-LIABILITY))))
+        (list "Income" (list (cons 'type ACCT-TYPE-INCOME)))
+        (list "A/Receivable" (list (cons 'type ACCT-TYPE-RECEIVABLE)))))
+
+(define (inv-tests variant)
+  ;; This function will perform implementation testing on the printable invoice.
+  (define uuid (cdr (assq variant uuid-list)))
+  (define (options->sxml options test-title)
+    (gnc:options->sxml uuid options (format #f "test-~a" variant) test-title))
+
+  (format #t "\n\n**** starting tests for variant ~a ****\n\n" variant)
+
+  (let* ((env (create-test-env))
+         (account-alist (env-create-account-structure-alist env structure))
+         (bank (cdr (assoc "Bank" account-alist)))
+         (income (cdr (assoc "Income" account-alist)))
+         (vat-sales (cdr (assoc "VAT-on-Sales" account-alist)))
+         (vat-purchases (cdr (assoc "VAT-on-Purchases" account-alist)))
+         (receivable (cdr (assoc "A/Receivable" account-alist)))
+         (YEAR (gnc:time64-get-year (gnc:get-today)))
+
+         (cust-1 (let ((cust-1 (gncCustomerCreate (gnc-get-current-book))))
+                   (gncCustomerSetID cust-1 "cust-1-id")
+                   (gncCustomerSetName cust-1 "cust-1-name")
+                   (gncCustomerSetNotes cust-1 "cust-1-notes")
+                   (gncCustomerSetCurrency cust-1 (gnc-default-report-currency))
+                   (gncCustomerSetTaxIncluded cust-1 1) ;1 = GNC-TAXINCLUDED-YES
+                   cust-1))
+
+         (owner-1 (let ((owner-1 (gncOwnerNew)))
+                    (gncOwnerInitCustomer owner-1 cust-1)
+                    owner-1))
+
+         ;; inv-1 is generated for a customer
+         (inv-1 (let ((inv-1 (gncInvoiceCreate (gnc-get-current-book))))
+                  (gncInvoiceSetOwner inv-1 owner-1)
+                  (gncInvoiceSetNotes inv-1 "inv-1-notes")
+                  (gncInvoiceSetBillingID inv-1 "inv-1-billing-id")
+                  inv-1))
+
+         (job-1 (let ((job-1 (gncJobCreate (gnc-get-current-book))))
+                  (gncJobSetID job-1 "job-1-id")
+                  (gncJobSetName job-1 "job-1-name")
+                  (gncJobSetOwner job-1 owner-1)
+                  job-1))
+
+         (owner-2 (let ((owner-2 (gncOwnerNew)))
+                    (gncOwnerInitJob owner-2 job-1)
+                    owner-2))
+
+         ;; inv-2 is generated from a customer's job
+         (inv-2 (let ((inv-2 (gncInvoiceCreate (gnc-get-current-book))))
+                  (gncInvoiceSetOwner inv-2 owner-2)
+                  (gncInvoiceSetNotes inv-2 "inv-2-notes")
+                  inv-2))
+
+         (vend-1 (let ((vend-1 (gncVendorCreate (gnc-get-current-book))))
+                   (gncVendorSetID vend-1 "vend-1-id")
+                   (gncVendorSetName vend-1 "vend-1-name")
+                   (gncVendorSetNotes vend-1 "vend-1-notes")
+                   (gncVendorSetCurrency vend-1 (gnc-default-report-currency))
+                   (gncVendorSetTaxIncluded vend-1 1) ;1 = GNC-TAXINCLUDED-YES
+                   vend-1))
+
+         (owner-3 (let ((owner-3 (gncOwnerNew)))
+                    (gncOwnerInitVendor owner-3 vend-1)
+                    owner-3))
+
+         ;; inv-3 is generated from a vendor
+         (inv-3 (let ((inv-3 (gncInvoiceCreate (gnc-get-current-book))))
+                  (gncInvoiceSetOwner inv-3 owner-3)
+                  (gncInvoiceSetNotes inv-3 "inv-3-notes")
+                  inv-3))
+
+         (emp-1 (let ((emp-1 (gncEmployeeCreate (gnc-get-current-book))))
+                  (gncEmployeeSetID emp-1 "emp-1-id")
+                  (gncEmployeeSetCurrency emp-1 (gnc-default-report-currency))
+                  (gncEmployeeSetName emp-1 "emp-1-name")
+                  emp-1))
+
+         (owner-4 (let ((owner-4 (gncOwnerNew)))
+                    (gncOwnerInitEmployee owner-4 emp-1)
+                    owner-4))
+
+         ;; inv-4 is generated for an employee
+         (inv-4 (let ((inv-4 (gncInvoiceCreate (gnc-get-current-book))))
+                  (gncInvoiceSetOwner inv-4 owner-4)
+                  (gncInvoiceSetNotes inv-4 "inv-4-notes")
+                  inv-4))
+
+         ;; inv-5 cust-credit-note
+         (inv-5 (let ((inv-5 (gncInvoiceCopy inv-1)))
+                  (gncInvoiceSetIsCreditNote inv-5 #t)
+                  inv-5))
+
+         ;; inv-6 vend-credit-note
+         (inv-6 (let ((inv-6 (gncInvoiceCopy inv-3)))
+                  (gncInvoiceSetIsCreditNote inv-6 #t)
+                  inv-6))
+
+         ;; inv-7 emp-credit-note
+         (inv-7 (let ((inv-7 (gncInvoiceCopy inv-4)))
+                  (gncInvoiceSetIsCreditNote inv-7 #t)
+                  inv-7))
+
+         (standard-vat-sales-tt (let ((tt (gncTaxTableCreate (gnc-get-current-book))))
+                                  (gncTaxTableIncRef tt)
+                                  (gncTaxTableSetName tt "10% vat on sales")
+                                  (let ((entry (gncTaxTableEntryCreate)))
+                                    (gncTaxTableEntrySetAccount entry vat-sales)
+                                    (gncTaxTableEntrySetType entry GNC-AMT-TYPE-PERCENT)
+                                    (gncTaxTableEntrySetAmount entry 10)
+                                    (gncTaxTableAddEntry tt entry))
+                                  tt))
+
+         (standard-vat-purchases-tt (let ((tt (gncTaxTableCreate (gnc-get-current-book))))
+                                      (gncTaxTableIncRef tt)
+                                      (gncTaxTableSetName tt "10% vat on purchases")
+                                      (let ((entry (gncTaxTableEntryCreate)))
+                                        (gncTaxTableEntrySetAccount entry vat-purchases)
+                                        (gncTaxTableEntrySetType entry GNC-AMT-TYPE-PERCENT)
+                                        (gncTaxTableEntrySetAmount entry 10)
+                                        (gncTaxTableAddEntry tt entry))
+                                      tt)))
+
+    (define (default-testing-options inv)
+      (let ((options (gnc:make-report-options uuid)))
+        (set-option! options "General" "Invoice Number" inv)
+        (for-each
+         (lambda (disp-col-name)
+           (set-option! options "Display Columns" disp-col-name #t))
+         '("Date" "Description" "Action" "Quantity" "Price" "Discount"
+           "Taxable" "Tax Amount" "Total"))
+        (for-each
+         (lambda (disp-col-name)
+           (set-option! options "Display" disp-col-name #t))
+         '("Individual Taxes" "Totals" "References" "Billing Terms"
+           "Billing ID" "Invoice Notes" "Payments" "Job Details"))
+        options))
+
+    ;; entry-1  2 widgets of $3 = $6
+    (let ((entry-1 (gncEntryCreate (gnc-get-current-book))))
+      (gncEntrySetDateGDate entry-1 (time64-to-gdate (current-time)))
+      (gncEntrySetDescription entry-1 "entry-1-desc")
+      (gncEntrySetAction entry-1 "entry-1-action")
+      (gncEntrySetNotes entry-1 "entry-1-notes")
+      (gncEntrySetInvAccount entry-1 income)
+      (gncEntrySetDocQuantity entry-1 2 #f)
+      (gncEntrySetInvPrice entry-1 3)
+      (gncInvoiceAddEntry inv-1 entry-1))
+
+    (test-begin "inv-1 simple entry")
+    (let* ((options (default-testing-options inv-1))
+           (sxml (options->sxml options "inv-1 simple entry")))
+      (test-equal "inv-1 simple entry amounts are correct"
+        '("$6.00" "$6.00" "$6.00" "$6.00")
+        (sxml-main-get-row-col sxml #f -1))
+      (test-equal "inv-1 simple entry details are correct"
+        '("entry-1-desc" "entry-1-action" "2.00" "$3.00" "0.00 %" "T" "$0.00" "$6.00")
+        (cdr (sxml-main-get-row-col sxml 1 #f)))
+      (test-equal "inv-1 cust-name is correct"
+        '("cust-1-name")
+        ((sxpath '(// (table 2) // tbody // tr // td // *text*))
+         sxml))
+      (test-assert "inv-1-billing-id is in invoice body"
+        (member
+         "Reference:\xa0inv-1-billing-id"
+         ((sxpath '(// body // *text*)) sxml)))
+      (test-assert "inv-1 inv-notes is in invoice body"
+        (member
+         "inv-1-notes"
+         ((sxpath '(// body // *text*)) sxml))))
+    (test-end "inv-1 simple entry")
+
+    (test-begin "inv-1 simple entry, sparse options")
+    (let* ((options (let ((options (default-testing-options inv-1)))
+                      (for-each
+                       (lambda (disp-col-name)
+                         (set-option! options "Display Columns" disp-col-name #f))
+                       '("Date" "Description" "Action" "Quantity" "Price" "Discount"
+                         "Taxable" "Tax Amount" "Total"))
+                      (for-each
+                       (lambda (disp-col-name)
+                         (set-option! options "Display" disp-col-name #f))
+                       '("Individual Taxes" "Totals" "References" "Billing Terms"
+                         "Billing ID" "Invoice Notes" "Payments" "Job Details"))
+                      options))
+           (sxml (options->sxml options "inv-1 simple entry sparse")))
+      (test-equal "inv-1 sparse simple entry headers are correct"
+        '("Net Price" "Tax" "Total Price" "Amount Due")
+        (sxml-main-get-row-col sxml #f 1))
+      (test-equal "inv-1 sparse simple entry amounts are correct"
+        '("$6.00" "$0.00" "$6.00" "$6.00")
+        (sxml-main-get-row-col sxml #f -1)))
+    (test-end "inv-1 simple entry, sparse options")
+
+    (test-begin "inv-2")
+    (let ((entry-2 (gncEntryCreate (gnc-get-current-book))))
+      (gncEntrySetDateGDate entry-2 (time64-to-gdate (current-time)))
+      (gncEntrySetDescription entry-2 "entry-2-desc")
+      (gncEntrySetAction entry-2 "entry-2-action")
+      (gncEntrySetNotes entry-2 "entry-2-notes")
+      (gncEntrySetInvAccount entry-2 income)
+      (gncEntrySetInvTaxable entry-2 #f)
+      (gncEntrySetDocQuantity entry-2 5 #f)
+      (gncEntrySetInvPrice entry-2 11)
+      (gncEntrySetInvDiscount entry-2 10)
+      (gncInvoiceAddEntry inv-1 entry-2))
+    ;; entry-inv-2  2 widgets of $3 = $6
+    (let ((entry-inv-2 (gncEntryCreate (gnc-get-current-book))))
+      (gncEntrySetDateGDate entry-inv-2 (time64-to-gdate (current-time)))
+      (gncEntrySetDescription entry-inv-2 "entry-inv-2-desc")
+      (gncEntrySetAction entry-inv-2 "entry-inv-2-action")
+      (gncEntrySetNotes entry-inv-2 "entry-inv-2-notes")
+      (gncEntrySetInvAccount entry-inv-2 income)
+      (gncEntrySetDocQuantity entry-inv-2 2 #f)
+      (gncEntrySetInvPrice entry-inv-2 3)
+      (gncInvoiceAddEntry inv-2 entry-inv-2))
+    (let* ((options (default-testing-options inv-2))
+           (sxml (options->sxml options "inv-2 simple entry")))
+      (test-equal "inv-2 simple entry amounts are correct"
+        '("$6.00" "$6.00" "$6.00" "$6.00")
+        (sxml-main-get-row-col sxml #f -1))
+      (test-equal "inv-2 simple entry details are correct"
+        '("entry-inv-2-desc" "entry-inv-2-action" "2.00" "$3.00" "0.00 %" "T" "$0.00" "$6.00")
+        (cdr (sxml-main-get-row-col sxml 1 #f)))
+      (test-equal "inv-2 cust-name is correct"
+        '("cust-1-name")
+        ((sxpath '(// (table 2) // tbody // tr // td // *text*))
+         sxml))
+      (test-assert "inv-2 inv-notes is in invoice body"
+        (member
+         "inv-2-notes"
+         ((sxpath '(// body // *text*)) sxml)))
+      (test-assert "inv-2 jobnumber is in invoice body"
+        (member
+         "Job number:\xa0job-1-id"
+         ((sxpath '(// body // *text*)) sxml)))
+      (test-assert "inv-2 jobname is in invoice body"
+        (member
+         "Job name:\xa0job-1-name"
+         ((sxpath '(// body // *text*)) sxml)))
+      )
+    (test-end "inv-2")
+
+    (test-begin "inv-3")
+    ;; entry-inv-3  2 widgets of $3 = $6
+    (let ((entry-inv-3 (gncEntryCreate (gnc-get-current-book))))
+      (gncEntrySetDateGDate entry-inv-3 (time64-to-gdate (current-time)))
+      (gncEntrySetDescription entry-inv-3 "entry-inv-3-desc")
+      (gncEntrySetAction entry-inv-3 "entry-inv-3-action")
+      (gncEntrySetNotes entry-inv-3 "entry-inv-3-notes")
+      (gncEntrySetInvAccount entry-inv-3 income)
+      (gncEntrySetDocQuantity entry-inv-3 2 #f)
+      (gncEntrySetBillPrice entry-inv-3 3)
+      (gncInvoiceAddEntry inv-3 entry-inv-3))
+    (let* ((options (default-testing-options inv-3))
+           (sxml (options->sxml options "inv-3 simple entry")))
+      (test-equal "inv-3 simple entry amounts are correct"
+        '("$6.00" "$6.00" "$6.00" "$6.00")
+        (sxml-main-get-row-col sxml #f -1))
+      (test-equal "inv-3 simple entry details are correct"
+        '("entry-inv-3-desc" "entry-inv-3-action" "2.00" "$3.00" "T" "$0.00" "$6.00")
+        (cdr (sxml-main-get-row-col sxml 1 #f)))
+      (test-equal "inv-3 vend-name is correct"
+        '("vend-1-name")
+        ((sxpath '(// (table 2) // tbody // tr // td // *text*))
+         sxml))
+      (test-assert "inv-3 inv-notes is in invoice body"
+        (member
+         "inv-3-notes"
+         ((sxpath '(// body // *text*)) sxml))))
+    (test-end "inv-3")
+
+
+    (test-begin "inv-4")
+    ;; entry-inv-4  2 widgets of $3 = $6
+    (let ((entry-inv-4 (gncEntryCreate (gnc-get-current-book))))
+      (gncEntrySetDateGDate entry-inv-4 (time64-to-gdate (current-time)))
+      (gncEntrySetDescription entry-inv-4 "entry-inv-4-desc")
+      (gncEntrySetAction entry-inv-4 "entry-inv-4-action")
+      (gncEntrySetNotes entry-inv-4 "entry-inv-4-notes")
+      (gncEntrySetInvAccount entry-inv-4 income)
+      (gncEntrySetDocQuantity entry-inv-4 2 #f)
+      (gncEntrySetBillPrice entry-inv-4 3)
+      (gncInvoiceAddEntry inv-4 entry-inv-4))
+    (let* ((options (default-testing-options inv-4))
+           (sxml (options->sxml options "inv-4 simple entry")))
+      (test-equal "inv-4 simple entry amounts are correct"
+        '("$6.00" "$6.00" "$6.00" "$6.00")
+        (sxml->table-row-col sxml 3 #f -1))
+      (test-equal "inv-4 simple entry details are correct"
+        '("entry-inv-4-desc" "entry-inv-4-action" "2.00" "$3.00" "T" "$0.00" "$6.00")
+        (cdr (sxml->table-row-col sxml 3 1 #f)))
+      (test-equal "inv-4 vend-name is correct"
+        '("emp-1-name" "emp-1-name")    ;FIXME: why is this duplicated????
+        ((sxpath '(// (table 2) // tbody // tr // td // *text*))
+         sxml))
+      (test-assert "inv-4 inv-notes is in invoice body"
+        (member
+         "inv-4-notes"
+         ((sxpath '(// body // *text*)) sxml))))
+    (test-end "inv-4")
+
+    (test-begin "inv-5 simple entry")
+    ;; entry-5  2 widgets of $3 = $6
+    (let ((entry-5 (gncEntryCreate (gnc-get-current-book))))
+      (gncEntrySetDateGDate entry-5 (time64-to-gdate (current-time)))
+      (gncEntrySetDescription entry-5 "entry-5-desc")
+      (gncEntrySetAction entry-5 "entry-5-action")
+      (gncEntrySetNotes entry-5 "entry-5-notes")
+      (gncEntrySetInvAccount entry-5 income)
+      (gncEntrySetDocQuantity entry-5 2 #t)
+      (gncEntrySetInvPrice entry-5 3)
+      (gncInvoiceAddEntry inv-5 entry-5))
+    (let* ((options (default-testing-options inv-5))
+           (sxml (options->sxml options "inv-5 simple entry")))
+      (test-equal "inv-5 simple entry amounts are correct"
+        '("$6.00" "$6.00" "$6.00" "$6.00")
+        (sxml-main-get-row-col sxml #f -1))
+      (test-equal "inv-5 simple entry details are correct"
+        '("entry-5-desc" "entry-5-action" "2.00" "$3.00" "0.00 %" "T" "$0.00" "$6.00")
+        (cdr (sxml-main-get-row-col sxml 1 #f)))
+      (test-equal "inv-5 cust-name is correct"
+        '("cust-1-name")
+        ((sxpath '(// (table 2) // tbody // tr // td // *text*))
+         sxml)))
+    (test-end "inv-5 simple entry")
+
+    (test-begin "inv-6")
+    (let ((entry-inv-6 (gncEntryCreate (gnc-get-current-book))))
+      (gncEntrySetDateGDate entry-inv-6 (time64-to-gdate (current-time)))
+      (gncEntrySetDescription entry-inv-6 "entry-inv-6-desc")
+      (gncEntrySetAction entry-inv-6 "entry-inv-6-action")
+      (gncEntrySetNotes entry-inv-6 "entry-inv-6-notes")
+      (gncEntrySetInvAccount entry-inv-6 income)
+      (gncEntrySetDocQuantity entry-inv-6 2 #t)
+      (gncEntrySetBillPrice entry-inv-6 3)
+      (gncInvoiceAddEntry inv-6 entry-inv-6))
+    (let* ((options (default-testing-options inv-6))
+           (sxml (options->sxml options "inv-6 simple entry")))
+      (test-equal "inv-6 simple entry amounts are correct"
+        '("$6.00" "$6.00" "$6.00" "$6.00")
+        (sxml-main-get-row-col sxml #f -1))
+      (test-equal "inv-6 simple entry details are correct"
+        '("entry-inv-6-desc" "entry-inv-6-action" "2.00" "$3.00" "T" "$0.00" "$6.00")
+        (cdr (sxml-main-get-row-col sxml 1 #f)))
+      (test-equal "inv-6 vend-name is correct"
+        '("vend-1-name")
+        ((sxpath '(// (table 2) // tbody // tr // td // *text*))
+         sxml))
+      (test-assert "inv-6 inv-3-notes is in invoice body"
+        (member
+         "inv-3-notes"
+         ((sxpath '(// body // *text*)) sxml))))
+    (test-end "inv-6")
+
+    (test-begin "inv-7")
+    ;; entry-inv-7  2 widgets of $3 = $6
+    (let ((entry-inv-7 (gncEntryCreate (gnc-get-current-book))))
+      (gncEntrySetDateGDate entry-inv-7 (time64-to-gdate (current-time)))
+      (gncEntrySetDescription entry-inv-7 "entry-inv-7-desc")
+      (gncEntrySetAction entry-inv-7 "entry-inv-7-action")
+      (gncEntrySetNotes entry-inv-7 "entry-inv-7-notes")
+      (gncEntrySetInvAccount entry-inv-7 income)
+      (gncEntrySetDocQuantity entry-inv-7 2 #t)
+      (gncEntrySetBillPrice entry-inv-7 3)
+      (gncInvoiceAddEntry inv-7 entry-inv-7))
+    (let* ((options (default-testing-options inv-7))
+           (sxml (options->sxml options "inv-7 simple entry")))
+      (test-equal "inv-7 simple entry amounts are correct"
+        '("$6.00" "$6.00" "$6.00" "$6.00")
+        (sxml-main-get-row-col sxml #f -1))
+      (test-equal "inv-7 simple entry details are correct"
+        '("entry-inv-7-desc" "entry-inv-7-action" "2.00" "$3.00" "T" "$0.00" "$6.00")
+        (cdr (sxml-main-get-row-col sxml 1 #f)))
+      (test-equal "inv-7 vend-name is correct"
+        '("emp-1-name" "emp-1-name")    ;FIXME: why is this duplicated????
+        ((sxpath '(// (table 2) // tbody // tr // td // *text*))
+         sxml))
+      (test-assert "inv-7 inv-4-notes is in invoice body"
+        (member
+         "inv-4-notes"
+         ((sxpath '(// body // *text*)) sxml))))
+    (test-end "inv-7")
+
+    (test-begin "combinations of gncEntry options")
+    (let* ((inv-8 (gncInvoiceCreate (gnc-get-current-book)))
+           (taxrate 109/10)
+           (discount 7/2)
+           (unitprice 777/4)
+           (quantity 11)
+           (combo-vat-sales-tt (let ((tt (gncTaxTableCreate (gnc-get-current-book))))
+                                 (gncTaxTableIncRef tt)
+                                 (gncTaxTableSetName tt (format #f "~a% vat on sales" taxrate))
+                                 (let ((entry (gncTaxTableEntryCreate)))
+                                   (gncTaxTableEntrySetAccount entry vat-sales)
+                                   (gncTaxTableEntrySetType entry GNC-AMT-TYPE-PERCENT)
+                                   (gncTaxTableEntrySetAmount entry taxrate)
+                                   (gncTaxTableAddEntry tt entry))
+                                 tt))
+           (billterm (let ((term (gncBillTermCreate (gnc-get-current-book))))
+                       (gncBillTermSetName term "billterm-name")
+                       (gncBillTermSetDescription term "billterm-desc")
+                       (gncBillTermSetType term 1) ;1 = GNC-TERM-TYPE-DAYS
+                       (gncBillTermSetDueDays term 8)
+                       term)))
+      (gncInvoiceSetOwner inv-8 owner-1)
+      (gncInvoiceSetCurrency inv-8 (gnc-default-report-currency))
+      (gncInvoiceSetTerms inv-8 billterm)
+      (for-each
+       (lambda (combo)
+         (let* ((each-entry (gncEntryCreate (gnc-get-current-book)))
+                (taxable? (= (vector-ref combo 0) 1))
+                (tax-included? (= (vector-ref combo 1) 1))
+                (discount-type (vector-ref combo 2))
+                (discount-how (vector-ref combo 3))
+                (desc (format #f "taxable=~a tax-included=~a discount-type=~a discount-how=~a"
+                              (if taxable? "Y" "N")
+                              (if tax-included? "Y" "N")
+                              (gncAmountTypeToString discount-type)
+                              (gncEntryDiscountHowToString discount-how))))
+           (gncEntrySetDateGDate each-entry (time64-to-gdate (current-time)))
+           (gncEntrySetDescription each-entry desc)
+           (gncEntrySetAction each-entry "action")
+           (gncEntrySetInvAccount each-entry income)
+           (gncEntrySetDocQuantity each-entry quantity #f)
+           (gncEntrySetInvPrice each-entry unitprice)
+           (gncEntrySetInvDiscount each-entry discount)
+           (gncEntrySetInvDiscountType each-entry discount-type)
+           (gncEntrySetInvDiscountHow each-entry discount-how)
+           (gncEntrySetInvTaxable each-entry taxable?)
+           (gncEntrySetInvTaxIncluded each-entry tax-included?)
+           (gncEntrySetInvTaxTable each-entry combo-vat-sales-tt)
+           (gncInvoiceAddEntry inv-8 each-entry)))
+       (list
+        ;; the following list specifies combinations to test gncEntry options
+        ;; thanks to rgmerk and to jenny for idea how to generate list of options
+        ;; (vector Taxable?(1=#t) Tax-included?(1=#t) DiscountType DiscountHow)
+        (vector 1 2 1 1)
+        (vector 2 1 2 2)
+        (vector 1 1 2 3)
+        (vector 2 2 1 3)
+        (vector 2 1 1 1)
+        (vector 1 2 2 2)
+        (vector 1 2 1 2)
+        (vector 1 1 2 1)))
+      (gncInvoiceSetNotes inv-8 (format #f "tax=~a%, discount=~a, qty=~a, price=~a" taxrate discount quantity unitprice))
+
+      (gncInvoicePostToAccount inv-8 receivable (current-time)
+                               (current-time) "trans-posting-memo"
+                               #t #f)
+
+      (gncInvoiceApplyPayment inv-8 '() bank 1747918/100 1
+                              (current-time) "trans-payment-memo-1" "trans-payment-num-1")
+      (let* ((options (default-testing-options inv-8))
+             (sxml (options->sxml options "inv-8 combinatorics")))
+        (test-assert "inv-8 billterm-desc is in invoice body"
+          (member
+           "Terms:\xa0billterm-desc"
+           ((sxpath '(// body // *text*)) sxml)))
+        (test-equal "inv-8 invoice date is in invoice body"
+          '("Invoice Date:\xa0")
+          (sxml->table-row-col sxml 2 1 1))
+        (test-equal "inv-8 due date is in invoice body"
+          '("Due Date:\xa0")
+          (sxml->table-row-col sxml 2 2 1))
+        (test-equal "inv-8 combo amounts are correct"
+          '("$2,133.25" "$2,061.96" "$2,133.25" "$2,061.96" "$2,133.25" "$2,133.25"
+            "$1,851.95" "$1,859.30" "$16,368.17" "$1,111.01" "$17,479.18"
+            "-$17,479.18" "$0.00")
+          (sxml->table-row-col sxml 4 #f -1))
+        ))
+    (test-end "combinations of gncEntry options")
+    ))

commit 7918c0317ea6d30df0829be86861cfb3292fb743
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun May 27 22:54:50 2018 +0800

    [test-extras.scm] options->sxml to whitespace
    
    Previously whitespace was encoded onto SXML, but it seems to make
    travis complain, i.e. on my branch no errors, however, on main Gnucash
    branch would occasionally strip whitespace. This change will parse
    XHTML and strip whitespace, which means some tests need amending.

diff --git a/gnucash/report/report-system/test/test-extras.scm b/gnucash/report/report-system/test/test-extras.scm
index 46023f3..bdbd92b 100644
--- a/gnucash/report/report-system/test/test-extras.scm
+++ b/gnucash/report/report-system/test/test-extras.scm
@@ -118,6 +118,7 @@
           (display render)))
       (catch 'parser-error
         (lambda () (xml->sxml render
+                              #:trim-whitespace? #t
                               #:entities '((nbsp . "\xa0"))))
         (lambda (k . args)
           (format #t "*** XML error. see render output at ~a\n~a"
diff --git a/gnucash/report/standard-reports/test/test-income-gst.scm b/gnucash/report/standard-reports/test/test-income-gst.scm
index a11bbf8..88f7786 100644
--- a/gnucash/report/standard-reports/test/test-income-gst.scm
+++ b/gnucash/report/standard-reports/test/test-income-gst.scm
@@ -188,15 +188,15 @@
       (set-option! options "Sorting" "Secondary Subtotal" 'account-name)
       (let ((sxml (options->sxml options "initial setup")))
         (test-equal "totals are as expected"
-          '("Grand Total" " " " " "$1,055.00" "$1,000.00" "$55.00" "$248.00" "$230.00" "$18.00")
+          '("Grand Total" "$1,055.00" "$1,000.00" "$55.00" "$248.00" "$230.00" "$18.00")
           (sxml->table-row-col sxml 1 -1 #f))
 
         (test-equal "tax on sales as expected"
-          '(" " "\n" "$20.00" "$20.00" " " " " "\n" "$20.00" "$20.00" "\n" "$15.00" "$15.00" "$55.00")
+          '("$20.00" "$20.00" "$20.00" "$20.00" "$15.00" "$15.00" "$55.00")
           (sxml->table-row-col sxml 1 #f 6))
 
         (test-equal "tax on purchases as expected"
-          '(" " " " " " " " "\n" "$8.00" "\n" "$10.00" "$18.00" " " " " "$18.00")
+          '("$8.00" "$10.00" "$18.00" "$18.00")
           (sxml->table-row-col sxml 1 #f 9)))
 
       (set-option! options "Display" "Individual tax columns" #t)
@@ -207,7 +207,7 @@
       (set-option! options "Display" "Tax payable" #t)
       (let ((sxml (options->sxml options "display options enabled")))
         (test-equal "all display columns enabled"
-          '("Grand Total" " " " " "$1,055.00" "$1,000.00" "$20.00" "$35.00" "$248.00" "$230.00" "$18.00" "$807.00" "$770.00" "$37.00")
+          '("Grand Total" "$1,055.00" "$1,000.00" "$20.00" "$35.00" "$248.00" "$230.00" "$18.00" "$807.00" "$770.00" "$37.00")
           (sxml->table-row-col sxml 1 -1 #f))))
 
     (test-end "display options")))
diff --git a/gnucash/report/standard-reports/test/test-transaction.scm b/gnucash/report/standard-reports/test/test-transaction.scm
index 7e4d0c7..5919ec7 100644
--- a/gnucash/report/standard-reports/test/test-transaction.scm
+++ b/gnucash/report/standard-reports/test/test-transaction.scm
@@ -277,9 +277,6 @@
       (test-equal "default headers"
         default-headers
         (get-row-col sxml 0 #f))
-      (test-equal "last row has same number of cols as header"
-        (length default-headers)
-        (length (get-row-col sxml -1 #f)))
       (test-equal "grand total present"
         '("Grand Total")
         (get-row-col sxml -1 1))
@@ -300,7 +297,7 @@
       (set-option! options "Sorting" "Secondary Subtotal for Date Key" 'monthly)
       (let ((sxml (options->sxml options "test basic column headers, and original currency")))
         (test-equal "default headers, indented, includes common-currency"
-          '(" " " " "Date" "Num" "Description" "Memo/Notes" "Account" "Amount (USD)" "Amount")
+          '("Date" "Num" "Description" "Memo/Notes" "Account" "Amount (USD)" "Amount")
           (get-row-col sxml 0 #f))
         (test-equal "grand total present, no blank cells, and is $2,280 in both common-currency and original-currency"
           '("Grand Total" "$2,280.00" "$2,280.00")
@@ -515,7 +512,7 @@
                  (string-null? (string-trim-both reconcile-date-string))))
            (get-row-col sxml #f 2)))
         (test-equal "reconciled status subtotal"
-          (list "Total For Unreconciled" " " " " " " " " " " " " " " " " "$0.00" " ")
+          (list "Total For Unreconciled" "$0.00")
           (get-row-col sxml -3 #f))
         )
 
@@ -642,10 +639,10 @@
                 "Debit (USD)" "Credit (USD)" "Debit" "Credit")
           (get-row-col sxml 0 #f))
         (test-equal "dual amount column, grand totals available"
-          (list "Grand Total" " " " " " " " " "$2,280.00" "$2,280.00")
+          (list "Grand Total" "$2,280.00" "$2,280.00")
           (get-row-col sxml -1 #f))
         (test-equal "dual amount column, first transaction correct"
-          (list "01/03/18" "$103 income" "Root.Asset.Bank" "\n" "$103.00" " " "\n" "$103.00" " ")
+          (list "01/03/18" "$103 income" "Root.Asset.Bank" "$103.00" "$103.00")
           (get-row-col sxml 1 #f)))
       )
 
@@ -743,10 +740,10 @@
       (set-option! options "Sorting" "Show Account Description" #t)
       (let* ((sxml (options->sxml options "sorting=date, friendly headers")))
         (test-equal "expense acc friendly headers"
-          '("\n" "Expenses" "\n" "Expense" "\n" "Rebate")
+          '("Expenses" "Expense" "Rebate")
           (get-row-col sxml 69 #f))
         (test-equal "income acc friendly headers"
-          '("\n" "Income" "\n" "Charge" "\n" "Income")
+          '("Income" "Charge" "Income")
           (get-row-col sxml 91 #f)))
 
       (set-option! options "Accounts" "Accounts" (list bank))

commit 746219926aa765a1df23f5436dd3d7ee02b60b6a
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun May 27 15:55:04 2018 +0800

    [test-extras.scm] upgrade options->sxml to parse   entities
    
    The default xml->sxml parser handles only > < & '
    " entities. We need to add handler for   as well.

diff --git a/gnucash/report/report-system/test/test-extras.scm b/gnucash/report/report-system/test/test-extras.scm
index b8a74c2..46023f3 100644
--- a/gnucash/report/report-system/test/test-extras.scm
+++ b/gnucash/report/report-system/test/test-extras.scm
@@ -117,7 +117,8 @@
         (lambda ()
           (display render)))
       (catch 'parser-error
-        (lambda () (xml->sxml render))
+        (lambda () (xml->sxml render
+                              #:entities '((nbsp . "\xa0"))))
         (lambda (k . args)
           (format #t "*** XML error. see render output at ~a\n~a"
                   filename (gnc:html-render-options-changed options #t))

commit b30f4d7c901884b1ce1d6ce8d0438551a3c42461
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun May 27 14:20:14 2018 +0800

    [easy-invoice.scm] fix html to be parsable by sxml
    
    Without this change the HTML cannot be parsed by sxml.

diff --git a/gnucash/report/business-reports/easy-invoice.scm b/gnucash/report/business-reports/easy-invoice.scm
index ecf14a2..351efc7 100644
--- a/gnucash/report/business-reports/easy-invoice.scm
+++ b/gnucash/report/business-reports/easy-invoice.scm
@@ -655,7 +655,7 @@
     ; framing table
     (add-html! document "<center><table width='")
     (add-html! document (opt-val "Display" "Invoice Width"))
-    (add-html! document "' cellpadding=0 cellspacing=0>")
+    (add-html! document "' cellpadding='0' cellspacing='0'>")
 
     (add-html! document "<tr><td align='left'>")
 

commit af0071256116d4aed1ff65d14aba572cd231d74f
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Tue Jun 12 22:41:11 2018 +0800

    Bug 796537 - Transaction Report cannot sort by "num"
    
    This is technically a bug in the TR regarding book split-action
    property. The reworked TR did not retrieve book split-action properly
    which means it was always assuming split-action was #f for sorting.
    
    This commit redefines sortkey-list to accept split-action? as a
    parameter to generate the correct sortkeys. Unfortunately it will make
    it more complicated; I'm not sure how to simplify.

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 2be2d86..11c3a74 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -117,7 +117,7 @@ in the Options panel."))
 
 (define SORTKEY-INFORMAL-HEADERS (list 'account-name 'account-code))
 
-(define sortkey-list
+(define (sortkey-list split-action?)
   ;;
   ;; Defines the different sorting keys, as an association-list
   ;; together with the subtotal functions. Each entry:
@@ -198,8 +198,7 @@ in the Options panel."))
                                    (cons 'tip (_ "Sort by description."))
                                    (cons 'renderer-fn (lambda (s) (xaccTransGetDescription (xaccSplitGetParent s))))))
 
-        (if (and (gnc-current-session-exist)
-                 (qof-book-use-split-action-for-num-field (gnc-get-current-book)))
+        (if split-action?
             (cons 'number    (list (cons 'sortkey (list SPLIT-ACTION))
                                    (cons 'split-sortvalue (lambda (a) (xaccSplitGetAction a)))
                                    (cons 'text (_ "Number/Action"))
@@ -419,20 +418,20 @@ Credit Card, and Income accounts."))
       (keylist-get-info keylist (car item) 'tip)))
    keylist))
 
-(define (SUBTOTAL-ENABLED? sortkey)
+(define (SUBTOTAL-ENABLED? sortkey split-action?)
   ;; this returns whether sortkey *can* be subtotalled/grouped.
   ;; it checks whether a renderer-fn is defined.
-  (keylist-get-info sortkey-list sortkey 'renderer-fn))
+  (keylist-get-info (sortkey-list split-action?) sortkey 'renderer-fn))
 
-(define (CUSTOM-SORTING? sortkey)
+(define (CUSTOM-SORTING? sortkey split-action?)
   ;; sortkey -> bool
   ;;
   ;; this returns which sortkeys which *must* use the custom sorter.
   ;; it filters whereby a split-sortvalue is defined (i.e. the splits
   ;; can be compared according to their 'sortvalue) but the QofQuery
   ;; sortkey is not defined (i.e. their 'sortkey is #f).
-  (and (keylist-get-info sortkey-list sortkey 'split-sortvalue)
-       (not (keylist-get-info sortkey-list sortkey 'sortkey))))
+  (and (keylist-get-info (sortkey-list split-action?) sortkey 'split-sortvalue)
+       (not (keylist-get-info (sortkey-list split-action?) sortkey 'sortkey))))
 
 ;;
 ;; Set defaults for reconcilation report
@@ -620,7 +619,7 @@ be excluded from periodic reporting.")
   ;; Sorting options
 
   (let ((ascending-choice-list (keylist->vectorlist ascending-list))
-        (key-choice-list (keylist->vectorlist sortkey-list))
+        (key-choice-list (keylist->vectorlist (sortkey-list BOOK-SPLIT-ACTION)))
         (date-subtotal-choice-list (keylist->vectorlist date-subtotal-list))
         (prime-sortkey 'account-name)
         (prime-sortkey-subtotal-true #t)
@@ -629,10 +628,10 @@ be excluded from periodic reporting.")
 
     (define (apply-selectable-by-name-sorting-options)
       (let* ((prime-sortkey-enabled (not (eq? prime-sortkey 'none)))
-             (prime-sortkey-subtotal-enabled (SUBTOTAL-ENABLED? prime-sortkey))
+             (prime-sortkey-subtotal-enabled (SUBTOTAL-ENABLED? prime-sortkey BOOK-SPLIT-ACTION))
              (prime-date-sortingtype-enabled (member prime-sortkey DATE-SORTING-TYPES))
              (sec-sortkey-enabled (not (eq? sec-sortkey 'none)))
-             (sec-sortkey-subtotal-enabled (SUBTOTAL-ENABLED? sec-sortkey))
+             (sec-sortkey-subtotal-enabled (SUBTOTAL-ENABLED? sec-sortkey BOOK-SPLIT-ACTION))
              (sec-date-sortingtype-enabled (member sec-sortkey DATE-SORTING-TYPES)))
 
         (gnc-option-db-set-option-selectable-by-name
@@ -1006,17 +1005,17 @@ be excluded from periodic reporting.")
     (let ((sortkey (opt-val pagename-sorting optname-prime-sortkey)))
       (if (member sortkey DATE-SORTING-TYPES)
           (keylist-get-info date-subtotal-list (opt-val pagename-sorting optname-prime-date-subtotal) info)
-          (and (SUBTOTAL-ENABLED? sortkey)
+          (and (SUBTOTAL-ENABLED? sortkey BOOK-SPLIT-ACTION)
                (opt-val pagename-sorting optname-prime-subtotal)
-               (keylist-get-info sortkey-list sortkey info)))))
+               (keylist-get-info (sortkey-list BOOK-SPLIT-ACTION) sortkey info)))))
 
   (define (secondary-get-info info)
     (let ((sortkey (opt-val pagename-sorting optname-sec-sortkey)))
       (if (member sortkey DATE-SORTING-TYPES)
           (keylist-get-info date-subtotal-list (opt-val pagename-sorting optname-sec-date-subtotal) info)
-          (and (SUBTOTAL-ENABLED? sortkey)
+          (and (SUBTOTAL-ENABLED? sortkey BOOK-SPLIT-ACTION)
                (opt-val pagename-sorting optname-sec-subtotal)
-               (keylist-get-info sortkey-list sortkey info)))))
+               (keylist-get-info (sortkey-list BOOK-SPLIT-ACTION) sortkey info)))))
 
   (let* ((work-to-do (length splits))
          (work-done 0)
@@ -1300,7 +1299,7 @@ be excluded from periodic reporting.")
                                   (gnc:make-html-text
                                    (gnc:html-markup-b
                                     ((vector-ref cell 5)
-                                     ((keylist-get-info sortkey-list sortkey 'renderer-fn) split))))))
+                                     ((keylist-get-info (sortkey-list BOOK-SPLIT-ACTION) sortkey 'renderer-fn) split))))))
                         calculated-cells))
             (addto! row-contents (gnc:make-html-table-cell/size
                                   1 (+ right-indent width-left-columns width-right-columns) data)))
@@ -1426,7 +1425,7 @@ be excluded from periodic reporting.")
 
     ;; generate account name, optionally with anchor to account register
     (define (render-account sortkey split anchor?)
-      (let* ((account ((keylist-get-info sortkey-list sortkey 'renderer-fn) split))
+      (let* ((account ((keylist-get-info (sortkey-list BOOK-SPLIT-ACTION) sortkey 'renderer-fn) split))
              (name (account-namestring account
                                        (column-uses? 'sort-account-code)
                                        #t
@@ -1443,7 +1442,7 @@ be excluded from periodic reporting.")
 
     ;; generic renderer. retrieve renderer-fn which should return a str
     (define (render-generic sortkey split)
-      ((keylist-get-info sortkey-list sortkey 'renderer-fn) split))
+      ((keylist-get-info (sortkey-list BOOK-SPLIT-ACTION) sortkey 'renderer-fn) split))
 
     (define (render-summary split level anchor?)
       (let ((sortkey (opt-val pagename-sorting
@@ -1842,8 +1841,8 @@ be excluded from periodic reporting.")
                                 (not (eq? primary-date-subtotal 'none)))  ; until qof-query
                            (and (member secondary-key DATE-SORTING-TYPES) ; is upgraded
                                 (not (eq? secondary-date-subtotal 'none)))
-                           (or (CUSTOM-SORTING? primary-key)
-                               (CUSTOM-SORTING? secondary-key))))
+                           (or (CUSTOM-SORTING? primary-key BOOK-SPLIT-ACTION)
+                               (CUSTOM-SORTING? secondary-key BOOK-SPLIT-ACTION))))
          (infobox-display (opt-val gnc:pagename-general optname-infobox-display))
          (query (qof-query-create-for-splits)))
 
@@ -1853,12 +1852,12 @@ be excluded from periodic reporting.")
       ;; ascend? specifies whether ascending or descending
       (let* ((comparator-function
               (if (memq sortkey DATE-SORTING-TYPES)
-                  (let ((date (keylist-get-info sortkey-list sortkey 'split-sortvalue))
+                  (let ((date (keylist-get-info (sortkey-list BOOK-SPLIT-ACTION) sortkey 'split-sortvalue))
                         (date-comparator (keylist-get-info date-subtotal-list date-subtotal-key 'date-sortvalue)))
                     (lambda (s)
                       (and date-comparator
                            (date-comparator (date s)))))
-                  (or (keylist-get-info sortkey-list sortkey 'split-sortvalue)
+                  (or (keylist-get-info (sortkey-list BOOK-SPLIT-ACTION) sortkey 'split-sortvalue)
                       (lambda (s) #f))))
              (value-of-X (comparator-function split-X))
              (value-of-Y (comparator-function split-Y))
@@ -1921,8 +1920,8 @@ be excluded from periodic reporting.")
           (if (not custom-sort?)
               (begin
                 (qof-query-set-sort-order query
-                                          (keylist-get-info sortkey-list primary-key 'sortkey)
-                                          (keylist-get-info sortkey-list secondary-key 'sortkey)
+                                          (keylist-get-info (sortkey-list BOOK-SPLIT-ACTION) primary-key 'sortkey)
+                                          (keylist-get-info (sortkey-list BOOK-SPLIT-ACTION) secondary-key 'sortkey)
                                           '())
                 (qof-query-set-sort-increasing query
                                                (eq? primary-order 'ascend)

commit 9e6760f7cb78b5c89d9ecf7cb6865e941083a0dd
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Jun 11 09:56:04 2018 -0700

    Bug 796527 - invalid currency on scheduled transactions
    
    1. Don't even check for price/exchange rate on template transactions,
    there's no point.
    
    2. Extract function get_transaction_currency:
    a. Check all split commodities are valid, abort transaction creation if
    not.
    b. If the template transaction's currency isn't used by any of the
    splits set the new transaction's currency to the first-found currency if
    there is one, otherwise to the first-found commodity.
    
    3. Fix a minor typo in a comment.

diff --git a/gnucash/register/ledger-core/split-register-control.c b/gnucash/register/ledger-core/split-register-control.c
index fe14cd2..de1bc8d 100644
--- a/gnucash/register/ledger-core/split-register-control.c
+++ b/gnucash/register/ledger-core/split-register-control.c
@@ -1322,6 +1322,13 @@ gnc_split_register_handle_exchange (SplitRegister *reg, gboolean force_dialog)
 
     ENTER("reg=%p, force_dialog=%s", reg, force_dialog ? "TRUE" : "FALSE" );
 
+    /* No point in setting a rate on a template transaction. */
+    if (reg->is_template)
+    {
+        LEAVE("Template transaction, rate makes no sense.");
+        return FALSE;
+    }
+
     /* Make sure we NEED this for this type of register */
     if (!gnc_split_reg_has_rate_cell (reg->type))
     {
diff --git a/libgnucash/app-utils/gnc-sx-instance-model.c b/libgnucash/app-utils/gnc-sx-instance-model.c
index 4e07d04..cb8160c 100644
--- a/libgnucash/app-utils/gnc-sx-instance-model.c
+++ b/libgnucash/app-utils/gnc-sx-instance-model.c
@@ -1140,20 +1140,86 @@ split_apply_exchange_rate (Split *split, GHashTable *bindings,
     xaccSplitSetAmount(split, amt); /* marks split dirty */
 
 }
+/* If the template_txn was created from the SX Editor then it has the default
+ * currency even if none of its splits do; if the template_txn was created from
+ * a non-currency register then it will be requesting backwards prices. Check
+ * that the template_txn currency is in at least one split; if it's not a
+ * currency and one of the splits is, use that currency. If there are no
+ * currencies at all assume that the user knew what they were doing and return
+ * the template_transaction's commodity.
+ *
+ * Since we're going through the split commodities anyway, check that they all
+ * have useable values. If we find an error return NULL as a signal to
+ * create_each_transaction_helper to bail out.
+ */
+
+static gnc_commodity*
+get_transaction_currency(SxTxnCreationData *creation_data,
+                         SchedXaction *sx, Transaction *template_txn)
+{
+    gnc_commodity *first_currency = NULL, *first_cmdty = NULL;
+    gboolean err_flag = FALSE, txn_cmdty_in_splits = FALSE;
+    gnc_commodity *txn_cmdty = xaccTransGetCurrency (template_txn);
+    GList* txn_splits = xaccTransGetSplitList (template_txn);
+
+    if (txn_cmdty)
+        g_debug("Template txn currency is %s.",
+                gnc_commodity_get_mnemonic (txn_cmdty));
+    else
+        g_debug("No template txn currency.");
+
+    for (;txn_splits; txn_splits = txn_splits->next)
+    {
+        Split* t_split = (Split*)txn_splits->data;
+        Account* split_account = NULL;
+        gnc_commodity *split_cmdty = NULL;
+        if (!_get_template_split_account(sx, t_split, &split_account,
+                                         creation_data->creation_errors))
+        {
+            err_flag = TRUE;
+            break;
+        }
+        split_cmdty = xaccAccountGetCommodity (split_account);
+        if (!txn_cmdty)
+            txn_cmdty = split_cmdty;
+        if (!first_cmdty)
+            first_cmdty = split_cmdty;
+        if (gnc_commodity_equal (split_cmdty, txn_cmdty))
+            txn_cmdty_in_splits = TRUE;
+        if (!first_currency && gnc_commodity_is_currency (split_cmdty))
+            first_currency = split_cmdty;
+    }
+    if (err_flag)
+    {
+        g_critical("Error in SX transaction [%s], split missing account: "
+                   "Creation aborted.", xaccSchedXactionGetName(sx));
+        return NULL;
+    }
+    if (first_currency &&
+        (!txn_cmdty_in_splits || !gnc_commodity_is_currency (txn_cmdty)))
+        return first_currency;
+    if (!txn_cmdty_in_splits)
+        return first_cmdty;
+    return txn_cmdty;
+}
 
 static gboolean
 create_each_transaction_helper(Transaction *template_txn, void *user_data)
 {
     Transaction *new_txn;
-    GList *txn_splits, *template_splits;
+    GList *txn_splits, *template_splits, *node;
     Split *copying_split;
-    gnc_commodity *first_cmdty = NULL;
-    gboolean err_flag = FALSE;
     SxTxnCreationData *creation_data = (SxTxnCreationData*)user_data;
     SchedXaction *sx = creation_data->instance->parent->sx;
+    gnc_commodity *txn_cmdty = get_transaction_currency (creation_data,
+                                                         sx, template_txn);
+
+    /* No txn_cmdty means there was a defective split. Bail. */
+    if (txn_cmdty == NULL)
+         return FALSE;
 
     /* FIXME: In general, this should [correctly] deal with errors such
-       as not finding the approrpiate Accounts and not being able to
+       as not finding the appropriate Accounts and not being able to
        parse the formula|credit/debit strings. */
 
     new_txn = xaccTransCloneNoKvp(template_txn);
@@ -1163,8 +1229,6 @@ create_each_transaction_helper(Transaction *template_txn, void *user_data)
             xaccTransGetDescription(new_txn),
             xaccSchedXactionGetName(sx));
 
-    g_debug("template txn currency is %s",
-            gnc_commodity_get_mnemonic(xaccTransGetCurrency (template_txn)));
 
     /* Bug#500427: copy the notes, if any */
     if (xaccTransGetNotes(template_txn) != NULL)
@@ -1188,6 +1252,14 @@ create_each_transaction_helper(Transaction *template_txn, void *user_data)
         return FALSE;
     }
 
+    if (txn_cmdty == NULL)
+    {
+        xaccTransDestroy(new_txn);
+        xaccTransCommitEdit(new_txn);
+        return FALSE;
+    }
+    xaccTransSetCurrency(new_txn, txn_cmdty);
+
     for (;
          txn_splits && template_splits;
          txn_splits = txn_splits->next, template_splits = template_splits->next)
@@ -1202,30 +1274,10 @@ create_each_transaction_helper(Transaction *template_txn, void *user_data)
         template_split = (Split*)template_splits->data;
         copying_split = (Split*)txn_splits->data;
 
-        if (!_get_template_split_account(sx, template_split, &split_acct,
-                                         creation_data->creation_errors))
-        {
-            err_flag = TRUE;
-            break;
-        }
+        _get_template_split_account(sx, template_split, &split_acct,
+                                    creation_data->creation_errors);
 
         split_cmdty = xaccAccountGetCommodity(split_acct);
-        if (first_cmdty == NULL)
-        {
-            /* Set new_txn currency to template_txn if we have one, else first
-             * split
-             */
-            if (xaccTransGetCurrency(template_txn))
-                xaccTransSetCurrency(new_txn,
-                                     xaccTransGetCurrency(template_txn));
-            else /* FIXME check for marker split */
-                xaccTransSetCurrency(new_txn, split_cmdty);
-
-            first_cmdty = xaccTransGetCurrency(new_txn);
-        }
-        g_debug("new txn currency is %s",
-                gnc_commodity_get_mnemonic(first_cmdty));
-
         xaccSplitSetAccount(copying_split, split_acct);
 
         {
@@ -1235,26 +1287,17 @@ create_each_transaction_helper(Transaction *template_txn, void *user_data)
             g_debug("value is %s for memo split '%s'",
                     gnc_numeric_to_string (final),
                     xaccSplitGetMemo (copying_split));
-            if (! gnc_commodity_equal(split_cmdty,
-                                      xaccTransGetCurrency (new_txn)))
+            if (! gnc_commodity_equal(split_cmdty, txn_cmdty))
             {
                 split_apply_exchange_rate(copying_split,
                                           creation_data->instance->variable_bindings,
-                                          first_cmdty, split_cmdty, &final);
+                                          txn_cmdty, split_cmdty, &final);
             }
 
             xaccSplitScrub(copying_split);
         }
     }
 
-    if (err_flag)
-    {
-        g_critical("Error in SX transaction [%s], creation aborted.",
-                   xaccSchedXactionGetName(sx));
-        xaccTransDestroy(new_txn);
-        xaccTransCommitEdit(new_txn);
-        return FALSE;
-    }
 
     {
 	qof_instance_set (QOF_INSTANCE (new_txn),

commit 5f53e2926ac0a302063cb8f53c82a3d8de153231
Author: goodvibes2 <goodchris96 at gmail.com>
Date:   Mon Jun 11 18:30:04 2018 +1000

    Use HINTS instead of PATHS to tell cmake where to look for GTEST + GMOCK
    files so it selects the paths in GTEST_ROOT + GMOCK_ROOT rather than the
    system libraries.

diff --git a/common/cmake_modules/GncAddTest.cmake b/common/cmake_modules/GncAddTest.cmake
index 169c426..ea126a7 100644
--- a/common/cmake_modules/GncAddTest.cmake
+++ b/common/cmake_modules/GncAddTest.cmake
@@ -125,9 +125,9 @@ function(gnc_gtest_configure)
     set(GMOCK_ROOT $ENV{GMOCK_ROOT})
   endif()
   find_path(GTEST_INCLUDE_DIR gtest/gtest.h
-    PATHS ${GTEST_ROOT}/include ${GMOCK_ROOT}/gtest/include /usr/include)
+    HINTS ${GTEST_ROOT}/include ${GMOCK_ROOT}/gtest/include /usr/include)
   find_path(GTEST_SRC_DIR src/gtest-all.cc
-    PATHS ${GTEST_ROOT} ${GMOCK_ROOT}/gtest /usr/src/gtest)
+    HINTS ${GTEST_ROOT} ${GMOCK_ROOT}/gtest /usr/src/gtest)
   find_library(GTEST_SHARED_LIB gtest)
   find_library(GTEST_MAIN_LIB gtest_main)
   if ((GTEST_SHARED_LIB OR GTEST_SRC_DIR) AND GTEST_INCLUDE_DIR)
@@ -147,17 +147,17 @@ function(gnc_gtest_configure)
 
   message(STATUS "Checking for GMOCK")
   find_path(GMOCK_INCLUDE_DIR gmock/gmock.h
-    PATHS ${GMOCK_ROOT}/include /usr/include)
+    HINTS ${GMOCK_ROOT}/include /usr/include)
   unset(GMOCK_SRC_DIR CACHE)
   find_library(GMOCK_SHARED_LIB gmock)
   find_library(GMOCK_MAIN_LIB gmock_main)
   find_path(GMOCK_SRC_DIR src/gmock-all.cc
-    PATHS ${GMOCK_ROOT} /usr/src/gmock)
+    HINTS ${GMOCK_ROOT} /usr/src/gmock)
   if (GMOCK_SRC_DIR)
     set(GMOCK_MAIN_SRC_DIR "${GMOCK_SRC_DIR}/src")
   else()
     find_path(GMOCK_SRC_DIR gmock-all.cc
-      PATHS ${GMOCK_ROOT} /usr/src/gmock)
+      HINTS ${GMOCK_ROOT} /usr/src/gmock)
     if (GMOCK_SRC_DIR)
       set(GMOCK_MAIN_SRC_DIR "${GMOCK_SRC_DIR}")
     endif()

commit 64837820edbb2eb6f6e4371f73b43f476edd48f0
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Sun Jun 10 14:26:39 2018 +0100

    Bug 795831 - When read only threshold set, dates are silently changed
    
    When you have specified a read only threshold, the date is silently
    changed if it's beyond the threshold. There was already code to present
    a dialog informing of this but was disabled so enable it.

diff --git a/gnucash/gnome-utils/dialog-utils.c b/gnucash/gnome-utils/dialog-utils.c
index aa7f877..203152f 100644
--- a/gnucash/gnome-utils/dialog-utils.c
+++ b/gnucash/gnome-utils/dialog-utils.c
@@ -333,15 +333,26 @@ gnc_gdate_in_valid_range (GDate *test_date, gboolean warn)
     GDate *max_date = g_date_new_dmy (1,1,10000);
     GDate *min_date;
     gboolean ret = FALSE;
+    gboolean max_date_ok = FALSE;
+    gboolean min_date_ok = FALSE;
 
     if (use_autoreadonly)
         min_date = qof_book_get_autoreadonly_gdate (gnc_get_current_book());
     else
         min_date = g_date_new_dmy (1,1,1400);
 
-    if ((g_date_compare (max_date, test_date) > 0) &&
-        (g_date_compare (min_date, test_date) <= 0))
-        ret = TRUE;
+    // max date
+    if (g_date_compare (max_date, test_date) > 0)
+        max_date_ok = TRUE;
+
+    // min date
+    if (g_date_compare (min_date, test_date) <= 0)
+        min_date_ok = TRUE;
+
+    if (use_autoreadonly && warn)
+        ret = max_date_ok;
+    else
+        ret = min_date_ok & max_date_ok;
 
     if (warn && !ret)
     {
diff --git a/gnucash/register/register-gnome/datecell-gnome.c b/gnucash/register/register-gnome/datecell-gnome.c
index 71bf525..f38374c 100644
--- a/gnucash/register/register-gnome/datecell-gnome.c
+++ b/gnucash/register/register-gnome/datecell-gnome.c
@@ -41,6 +41,7 @@
 
 #include "datecell.h"
 #include "dialog-utils.h"
+#include "gnc-ui.h"
 #include "gnc-ui-util.h"
 #include "gnucash-date-picker.h"
 #include "gnucash-item-edit.h"
@@ -92,34 +93,35 @@ static gboolean gnc_date_cell_enter (BasicCell *bcell,
 static void gnc_date_cell_leave (BasicCell *bcell);
 
 static gboolean
-check_readonly_threshold (const gchar *datestr, GDate *d)
+check_readonly_threshold (const gchar *datestr, GDate *d, gboolean warn)
 {
     GDate *readonly_threshold = qof_book_get_autoreadonly_gdate(gnc_get_current_book());
     if (g_date_compare(d, readonly_threshold) < 0)
     {
-#if 0
-    gchar *dialog_msg = _("The entered date of the new transaction is "
-                  "older than the \"Read-Only Threshold\" set for "
-                  "this book. This setting can be changed in "
-                  "File -> Properties -> Accounts.");
-    gchar *dialog_title = _("Cannot store a transaction at this date");
-    GtkWidget *dialog = gtk_message_dialog_new(NULL,
-                           0,
-                           GTK_MESSAGE_ERROR,
-                           GTK_BUTTONS_OK,
-                           "%s", dialog_title);
-    gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
-                         "%s", dialog_msg);
-    gtk_dialog_run(GTK_DIALOG(dialog));
-    gtk_widget_destroy(dialog);
-#endif
-    g_warning("Entered date %s is before the \"auto-read-only threshold\";"
-          " resetting to the threshold.", datestr);
-
-    // Reset the date to the threshold date
-    g_date_set_julian (d, g_date_get_julian (readonly_threshold));
-    g_date_free (readonly_threshold);
-    return TRUE;
+        if (warn)
+        {
+            gchar *dialog_msg = _("The entered date of the transaction is "
+                          "older than the \"Read-Only Threshold\" set for "
+                          "this book. This setting can be changed in "
+                          "File -> Properties -> Accounts, resetting to the threshold.");
+            gchar *dialog_title = _("Cannot store a transaction at this date");
+            GtkWidget *dialog = gtk_message_dialog_new(gnc_ui_get_main_window (NULL),
+                                   0,
+                                   GTK_MESSAGE_ERROR,
+                                   GTK_BUTTONS_OK,
+                                   "%s", dialog_title);
+            gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG(dialog),
+                                 "%s", dialog_msg);
+            gtk_dialog_run (GTK_DIALOG(dialog));
+            gtk_widget_destroy (dialog);
+
+//        g_warning("Entered date %s is before the \"auto-read-only threshold\";"
+//              " resetting to the threshold.", datestr);
+        }
+        // Reset the date to the threshold date
+        g_date_set_julian (d, g_date_get_julian (readonly_threshold));
+        g_date_free (readonly_threshold);
+        return TRUE;
     }
     g_date_free (readonly_threshold);
     return FALSE;
@@ -161,14 +163,13 @@ gnc_parse_date (struct tm *parsed, const char * datestr, gboolean warn)
     // older than the threshold.
     if (use_autoreadonly)
     {
-        GDate *d = g_date_new_dmy(day, month, year);
-        if (check_readonly_threshold (datestr, d))
+        g_date_set_dmy (test_date, day, month, year);
+        if (check_readonly_threshold (datestr, test_date, warn))
         {
-            day = g_date_get_day (d);
-            month = g_date_get_month (d);
-            year = g_date_get_year (d);
+            day = g_date_get_day (test_date);
+            month = g_date_get_month (test_date);
+            year = g_date_get_year (test_date);
         }
-        g_date_free (d);
     }
     g_date_free (test_date);
 

commit 2384af6068741641487316b83a6ac5de1454989c
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Sun Jun 10 14:24:34 2018 +0100

    Add a dialog to warn users that date is out of range
    
    When the date is out of range present a dialog advising this and also
    advise that date will be reset to this year.

diff --git a/gnucash/gnome-utils/dialog-utils.c b/gnucash/gnome-utils/dialog-utils.c
index 0cd369e..aa7f877 100644
--- a/gnucash/gnome-utils/dialog-utils.c
+++ b/gnucash/gnome-utils/dialog-utils.c
@@ -39,6 +39,7 @@
 #include "gnc-path.h"
 #include "gnc-engine.h"
 #include "gnc-euro.h"
+#include "gnc-ui.h"
 #include "gnc-ui-util.h"
 #include "gnc-prefs.h"
 #include "gnc-combott.h"
@@ -325,16 +326,16 @@ gnc_draw_arrow_cb (GtkWidget *widget, cairo_t *cr, gpointer direction)
 }
 
 
-static gboolean
-gnc_gdate_in_valid_range (GDate *test_date)
+gboolean
+gnc_gdate_in_valid_range (GDate *test_date, gboolean warn)
 {
-    gboolean use_autoreadonly = qof_book_uses_autoreadonly(gnc_get_current_book());
+    gboolean use_autoreadonly = qof_book_uses_autoreadonly (gnc_get_current_book());
     GDate *max_date = g_date_new_dmy (1,1,10000);
     GDate *min_date;
     gboolean ret = FALSE;
 
     if (use_autoreadonly)
-        min_date = qof_book_get_autoreadonly_gdate(gnc_get_current_book());
+        min_date = qof_book_get_autoreadonly_gdate (gnc_get_current_book());
     else
         min_date = g_date_new_dmy (1,1,1400);
 
@@ -342,6 +343,21 @@ gnc_gdate_in_valid_range (GDate *test_date)
         (g_date_compare (min_date, test_date) <= 0))
         ret = TRUE;
 
+    if (warn && !ret)
+    {
+        gchar *dialog_msg = _("The entered date is out of the range "
+                  "01/01/1400 - 31/12/9999, resetting to this year");
+        gchar *dialog_title = _("Date out of range");
+        GtkWidget *dialog = gtk_message_dialog_new (gnc_ui_get_main_window (NULL),
+                               0,
+                               GTK_MESSAGE_ERROR,
+                               GTK_BUTTONS_OK,
+                               "%s", dialog_title);
+        gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG(dialog),
+                             "%s", dialog_msg);
+        gtk_dialog_run (GTK_DIALOG(dialog));
+        gtk_widget_destroy (dialog);
+    }
     g_date_free (max_date);
     g_date_free (min_date);
     return ret;
@@ -392,7 +408,7 @@ gnc_handle_date_accelerator (GdkEventKey *event,
         else
             g_date_add_days (&gdate, 1);
 
-        if (gnc_gdate_in_valid_range (&gdate))
+        if (gnc_gdate_in_valid_range (&gdate, FALSE))
             g_date_to_struct_tm (&gdate, tm);
         return TRUE;
 
@@ -428,7 +444,7 @@ gnc_handle_date_accelerator (GdkEventKey *event,
         else
             g_date_subtract_days (&gdate, 1);
 
-        if (gnc_gdate_in_valid_range (&gdate))
+        if (gnc_gdate_in_valid_range (&gdate, FALSE))
             g_date_to_struct_tm (&gdate, tm);
         return TRUE;
 
@@ -499,7 +515,7 @@ gnc_handle_date_accelerator (GdkEventKey *event,
     default:
         return FALSE;
     }
-    if (gnc_gdate_in_valid_range (&gdate))
+    if (gnc_gdate_in_valid_range (&gdate, FALSE))
         g_date_to_struct_tm (&gdate, tm);
 
     return TRUE;
diff --git a/gnucash/gnome-utils/dialog-utils.h b/gnucash/gnome-utils/dialog-utils.h
index 06d7ff8..37cb849 100644
--- a/gnucash/gnome-utils/dialog-utils.h
+++ b/gnucash/gnome-utils/dialog-utils.h
@@ -98,6 +98,8 @@ void gnc_widget_set_style_context (GtkWidget *widget, const char *gnc_class);
 \********************************************************************/
 gboolean gnc_draw_arrow_cb (GtkWidget *widget, cairo_t *cr, gpointer direction);
 
+gboolean gnc_gdate_in_valid_range (GDate *test_date, gboolean warn);
+
 gboolean gnc_handle_date_accelerator (GdkEventKey *event,
                                       struct tm *tm,
                                       const char *date_str);
diff --git a/gnucash/register/register-gnome/datecell-gnome.c b/gnucash/register/register-gnome/datecell-gnome.c
index 47bdd8d..71bf525 100644
--- a/gnucash/register/register-gnome/datecell-gnome.c
+++ b/gnucash/register/register-gnome/datecell-gnome.c
@@ -126,10 +126,11 @@ check_readonly_threshold (const gchar *datestr, GDate *d)
 }
 
 static void
-gnc_parse_date (struct tm *parsed, const char * datestr)
+gnc_parse_date (struct tm *parsed, const char * datestr, gboolean warn)
 {
     int day, month, year;
     gboolean use_autoreadonly = qof_book_uses_autoreadonly(gnc_get_current_book());
+    GDate *test_date;
 
     if (!parsed) return;
     if (!datestr) return;
@@ -146,6 +147,16 @@ gnc_parse_date (struct tm *parsed, const char * datestr)
         year = tm_today.tm_year + 1900;
     }
 
+    test_date = g_date_new_dmy (day, month, year);
+
+    if (!gnc_gdate_in_valid_range (test_date, warn))
+    {
+        struct tm tm_today;
+        memset (&tm_today, 0, sizeof (struct tm));
+        gnc_tm_get_today_start (&tm_today);
+        year = tm_today.tm_year + 1900;
+    }
+
     // If we have an auto-read-only threshold, do not accept a date that is
     // older than the threshold.
     if (use_autoreadonly)
@@ -159,6 +170,7 @@ gnc_parse_date (struct tm *parsed, const char * datestr)
         }
         g_date_free (d);
     }
+    g_date_free (test_date);
 
     parsed->tm_mday = day;
     parsed->tm_mon  = month - 1;
@@ -452,7 +464,7 @@ gnc_date_cell_commit (DateCell *cell)
     if (!cell)
         return;
 
-    gnc_parse_date (&(box->date), cell->cell.value);
+    gnc_parse_date (&(box->date), cell->cell.value, FALSE);
 
     qof_print_date_dmy_buff (buff, MAX_DATE_LENGTH,
                              box->date.tm_mday,
@@ -581,9 +593,8 @@ gnc_date_cell_modify_verify (BasicCell *_cell,
     /* keep a copy of the new value */
     if (accept)
     {
-
         gnc_basic_cell_set_value_internal (&cell->cell, newval);
-        gnc_parse_date (&(box->date), newval);
+        gnc_parse_date (&(box->date), newval, FALSE);
 
         if (!box->date_picker)
             return;
@@ -710,7 +721,7 @@ gnc_date_cell_get_date_gdate (DateCell *cell, GDate *date)
     if (!cell || !date)
         return;
 
-    gnc_parse_date (&(box->date), cell->cell.value);
+    gnc_parse_date (&(box->date), cell->cell.value, FALSE);
 
     g_date_set_dmy(date,
                    box->date.tm_mday,
@@ -724,7 +735,8 @@ gnc_date_cell_get_date (DateCell *cell, time64 *time)
     PopBox *box = cell->cell.gui_private;
     if (!cell || !time)
         return;
-    gnc_parse_date (&(box->date), cell->cell.value);
+
+    gnc_parse_date (&(box->date), cell->cell.value, TRUE);
     *time = gnc_mktime (&box->date);
 }
 
@@ -735,7 +747,7 @@ gnc_date_cell_set_value_internal (BasicCell *_cell, const char *str)
     PopBox *box = cell->cell.gui_private;
     char buff[DATE_BUF];
 
-    gnc_parse_date (&(box->date), str);
+    gnc_parse_date (&(box->date), str, FALSE);
 
     qof_print_date_dmy_buff (buff, MAX_DATE_LENGTH,
                              box->date.tm_mday,

commit a73f9123f9fc936bbbfe0ea05b0cc06c52b9ab6e
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Sat Jun 9 15:10:12 2018 +0100

    Fix some indentation in datecell-gnome.c

diff --git a/gnucash/register/register-gnome/datecell-gnome.c b/gnucash/register/register-gnome/datecell-gnome.c
index 604933c..47bdd8d 100644
--- a/gnucash/register/register-gnome/datecell-gnome.c
+++ b/gnucash/register/register-gnome/datecell-gnome.c
@@ -139,7 +139,7 @@ gnc_parse_date (struct tm *parsed, const char * datestr)
         // Couldn't parse date, use today
         struct tm tm_today;
 
-    memset (&tm_today, 0, sizeof (struct tm));
+        memset (&tm_today, 0, sizeof (struct tm));
         gnc_tm_get_today_start (&tm_today);
         day = tm_today.tm_mday;
         month = tm_today.tm_mon + 1;
@@ -151,13 +151,13 @@ gnc_parse_date (struct tm *parsed, const char * datestr)
     if (use_autoreadonly)
     {
         GDate *d = g_date_new_dmy(day, month, year);
-    if (check_readonly_threshold (datestr, d))
-    {
-        day = g_date_get_day (d);
-        month = g_date_get_month (d);
-        year = g_date_get_year (d);
-    }
-    g_date_free (d);
+        if (check_readonly_threshold (datestr, d))
+        {
+            day = g_date_get_day (d);
+            month = g_date_get_month (d);
+            year = g_date_get_year (d);
+        }
+        g_date_free (d);
     }
 
     parsed->tm_mday = day;

commit 1f14d0f6296e69ace72ccc9f7042f89d74a16e55
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Sat Jun 9 15:04:27 2018 +0100

    In qof_scan_date returns valid date when day and month are 0
    
    If you start typing a date in the register with the enable autoreadonly
    option starting with a 0, gdate complains when trying to create a valid
    date.

diff --git a/libgnucash/engine/gnc-date.cpp b/libgnucash/engine/gnc-date.cpp
index edd6eac..3107f1a 100644
--- a/libgnucash/engine/gnc-date.cpp
+++ b/libgnucash/engine/gnc-date.cpp
@@ -936,6 +936,9 @@ qof_scan_date_internal (const char *buff, int *day, int *month, int *year,
 
     g_free (dupe);
 
+    if ((imonth == 0) || (iday == 0))
+        return FALSE;
+
     if ((12 < imonth) || (31 < iday))
     {
         /*

commit 00e6ccdda0fbc5bccbc9167e3f224a8200617061
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Sat Jun 9 15:01:30 2018 +0100

    Bug 796398 - Restrict accelerator keys to valid date range
    
    Valid date range is 01/01/1400 - 31/12/9999 so only allow accelerator
    keys in this range.

diff --git a/gnucash/gnome-utils/dialog-utils.c b/gnucash/gnome-utils/dialog-utils.c
index f6db0e0..0cd369e 100644
--- a/gnucash/gnome-utils/dialog-utils.c
+++ b/gnucash/gnome-utils/dialog-utils.c
@@ -325,6 +325,29 @@ gnc_draw_arrow_cb (GtkWidget *widget, cairo_t *cr, gpointer direction)
 }
 
 
+static gboolean
+gnc_gdate_in_valid_range (GDate *test_date)
+{
+    gboolean use_autoreadonly = qof_book_uses_autoreadonly(gnc_get_current_book());
+    GDate *max_date = g_date_new_dmy (1,1,10000);
+    GDate *min_date;
+    gboolean ret = FALSE;
+
+    if (use_autoreadonly)
+        min_date = qof_book_get_autoreadonly_gdate(gnc_get_current_book());
+    else
+        min_date = g_date_new_dmy (1,1,1400);
+
+    if ((g_date_compare (max_date, test_date) > 0) &&
+        (g_date_compare (min_date, test_date) <= 0))
+        ret = TRUE;
+
+    g_date_free (max_date);
+    g_date_free (min_date);
+    return ret;
+}
+
+
 gboolean
 gnc_handle_date_accelerator (GdkEventKey *event,
                              struct tm *tm,
@@ -368,7 +391,9 @@ gnc_handle_date_accelerator (GdkEventKey *event,
             g_date_add_years (&gdate, 1);
         else
             g_date_add_days (&gdate, 1);
-        g_date_to_struct_tm (&gdate, tm);
+
+        if (gnc_gdate_in_valid_range (&gdate))
+            g_date_to_struct_tm (&gdate, tm);
         return TRUE;
 
     case GDK_KEY_minus:
@@ -402,7 +427,9 @@ gnc_handle_date_accelerator (GdkEventKey *event,
             g_date_subtract_years (&gdate, 1);
         else
             g_date_subtract_days (&gdate, 1);
-        g_date_to_struct_tm (&gdate, tm);
+
+        if (gnc_gdate_in_valid_range (&gdate))
+            g_date_to_struct_tm (&gdate, tm);
         return TRUE;
 
     default:
@@ -472,8 +499,8 @@ gnc_handle_date_accelerator (GdkEventKey *event,
     default:
         return FALSE;
     }
-
-    g_date_to_struct_tm (&gdate, tm);
+    if (gnc_gdate_in_valid_range (&gdate))
+        g_date_to_struct_tm (&gdate, tm);
 
     return TRUE;
 }

commit 6f0a3c4345161e3ae005afb6bf4c7c49486587a6
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Jun 8 16:23:43 2018 -0700

    Bug 795362 - Special variable "i" not parsed in function calls
    
    Limit the range of the random value to 1..1000 to prevent overflows,
    particularly in number-of-periods or number-of-years variables.
    
    While we're at it, g_random_int and g_random_int_range return ints so
    piping the result through gnc_double_to_numeric() doesn't make much
    sense. That's removed, we just construct a gnc_numeric.

diff --git a/libgnucash/app-utils/gnc-sx-instance-model.c b/libgnucash/app-utils/gnc-sx-instance-model.c
index 764ada1..4e07d04 100644
--- a/libgnucash/app-utils/gnc-sx-instance-model.c
+++ b/libgnucash/app-utils/gnc-sx-instance-model.c
@@ -328,9 +328,13 @@ gnc_sx_get_variables(SchedXaction *sx, GHashTable *var_hash)
 static void
 _set_var_to_random_value(gchar *key, GncSxVariable *var, gpointer unused_user_data)
 {
-    var->value = double_to_gnc_numeric(g_random_int() + 2, 1,
-                                       GNC_NUMERIC_RND_MASK
-                                       | GNC_HOW_RND_FLOOR);
+    /* This is used by dialog-sx-editor to plug in values as a simplistic way to
+     * check balances. One possible variable is the number of periods in a
+     * interest or future value calculation where the variable is used as an
+     * exponent, so we want the numbers to be monotonically > 0 and not so large
+     * that they'll cause overflows.
+     */
+    var->value = gnc_numeric_create(g_random_int_range(1, 1000), 1);
 }
 
 void

commit dc7135920b0c0e2e20f98ab12e9d78b2961afceb
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Fri Jun 8 14:27:58 2018 +0200

    Minor build error message improvement

diff --git a/common/cmake_modules/MakeDist.cmake b/common/cmake_modules/MakeDist.cmake
index 3cbaab5..2db3aee 100644
--- a/common/cmake_modules/MakeDist.cmake
+++ b/common/cmake_modules/MakeDist.cmake
@@ -97,7 +97,7 @@ function(make_dist PACKAGE_PREFIX GNUCASH_SOURCE_DIR BUILD_SOURCE_DIR BUILDING_F
 endfunction()
 
 if (NOT WITH_GNUCASH)
-    message(SEND_ERROR "Creation of dist tarballs not support when WITH_GNUCASH=OFF.")
+    message(SEND_ERROR "Creation of dist tarballs is not supported when WITH_GNUCASH=OFF.")
 endif()
 
  make_dist(${PACKAGE_PREFIX} ${GNUCASH_SOURCE_DIR} ${BUILD_SOURCE_DIR} ${BUILDING_FROM_VCS})

commit cdcb230a3183d119bd8c039e11d576cfc32b5509
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Jun 7 17:38:04 2018 -0700

    Don't look for backend libraries in old autotools subdirs.
    
    Partly addresses Bug 794526

diff --git a/libgnucash/engine/gnc-engine.c b/libgnucash/engine/gnc-engine.c
index 171e3ef..fb68e44 100644
--- a/libgnucash/engine/gnc-engine.c
+++ b/libgnucash/engine/gnc-engine.c
@@ -74,9 +74,9 @@ gnc_engine_init_part2()
     } libs[] =
     {
 #if defined( HAVE_DBI_DBI_H )
-        { "dbi", "gncmod-backend-dbi", TRUE },
+        { "", "gncmod-backend-dbi", TRUE },
 #endif
-        { "xml", "gncmod-backend-xml", TRUE },
+        { "", "gncmod-backend-xml", TRUE },
         { NULL, NULL, FALSE }
     }, *lib;
 

commit e3e1464a018d8356d69303f8df6220f11f9dcddc
Author: Potuz <potuz at potuz.net>
Date:   Thu Jun 7 14:17:49 2018 -0700

    Bug 794617 - Can't compile with -DWITH_GNUCASH=NO due to scm-gnome-utils
    
    Pricedb.go doesn't need gnc-gnome-utils and we don't need WebKit or
    gwenhywfar-gtk3 if we have no GUI.

diff --git a/CMakeLists.txt b/CMakeLists.txt
index b8c3998..989d579 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -198,32 +198,32 @@ gnc_pkg_check_modules (GTHREAD REQUIRED gthread-2.0>=2.20)
 
 gnc_pkg_check_modules (LIBXML2 REQUIRED libxml-2.0>=2.7.0)
 gnc_pkg_check_modules (LIBXSLT REQUIRED libxslt)
-if (WIN32 OR APPLE)
-  gnc_pkg_check_modules (WEBKIT1 REQUIRED webkitgtk-3.0)
-  set(WEBKIT1 1)
+if (WITH_GNUCASH)
+  if (WIN32 OR APPLE)
+    gnc_pkg_check_modules (WEBKIT1 REQUIRED webkitgtk-3.0)
+    set(WEBKIT1 1)
     set(WEBKIT_CFLAGS ${WEBKIT2_CFLAGS})
     set(WEBKIT_INCLUDE_DIRS ${WEBKIT1_INCLUDE_DIRS})
     set(WEBKIT_LDFLAGS ${WEBKIT1_LDFLAGS})
     set(WEBKIT_LIBRARIES ${WEBKIT1_LIBRARIES})
-else (WIN32 OR APPLE)
-  gnc_pkg_check_modules (WEBKIT2_4 webkit2gtk-4.0)
-  if (NOT WEBKIT2_4_FOUND)
-    gnc_pkg_check_modules (WEBKIT2_3 REQUIRED webkit2gtk-3.0)
-    set(WEBKIT2_3 1)
-    set(WEBKIT_CFLAGS ${WEBKIT2_3_CFLAGS})
-    set(WEBKIT_INCLUDE_DIRS ${WEBKIT2_3_INCLUDE_DIRS})
-    set(WEBKIT_LDFLAGS ${WEBKIT2_3_LDFLAGS})
-    set(WEBKIT_LIBRARIES ${WEBKIT2_3_LIBRARIES})
-  else (NOT WEBKIT2_4_FOUND)
-    set(WEBKIT2_4 1)
-    set(WEBKIT_CFLAGS ${WEBKIT2_4_CFLAGS})
-    set(WEBKIT_INCLUDE_DIRS ${WEBKIT2_4_INCLUDE_DIRS})
-    set(WEBKIT_LDFLAGS ${WEBKIT2_4_LDFLAGS})
-    set(WEBKIT_LIBRARIES ${WEBKIT2_4_LIBRARIES})
-  endif (NOT WEBKIT2_4_FOUND)
-endif (WIN32 OR APPLE)
+  else (WIN32 OR APPLE)
+    gnc_pkg_check_modules (WEBKIT2_4 webkit2gtk-4.0)
+    if (NOT WEBKIT2_4_FOUND)
+      gnc_pkg_check_modules (WEBKIT2_3 REQUIRED webkit2gtk-3.0)
+      set(WEBKIT2_3 1)
+      set(WEBKIT_CFLAGS ${WEBKIT2_3_CFLAGS})
+      set(WEBKIT_INCLUDE_DIRS ${WEBKIT2_3_INCLUDE_DIRS})
+      set(WEBKIT_LDFLAGS ${WEBKIT2_3_LDFLAGS})
+      set(WEBKIT_LIBRARIES ${WEBKIT2_3_LIBRARIES})
+    else (NOT WEBKIT2_4_FOUND)
+      set(WEBKIT2_4 1)
+      set(WEBKIT_CFLAGS ${WEBKIT2_4_CFLAGS})
+      set(WEBKIT_INCLUDE_DIRS ${WEBKIT2_4_INCLUDE_DIRS})
+      set(WEBKIT_LDFLAGS ${WEBKIT2_4_LDFLAGS})
+      set(WEBKIT_LIBRARIES ${WEBKIT2_4_LIBRARIES})
+    endif (NOT WEBKIT2_4_FOUND)
+  endif (WIN32 OR APPLE)
 
-if (WITH_GNUCASH)
   gnc_pkg_check_modules (GTK3 REQUIRED gtk+-3.0>=3.10.0)
 endif (WITH_GNUCASH)
 
diff --git a/borrowed/CMakeLists.txt b/borrowed/CMakeLists.txt
index f9988c6..421d1fe 100644
--- a/borrowed/CMakeLists.txt
+++ b/borrowed/CMakeLists.txt
@@ -1,7 +1,9 @@
 add_subdirectory(libc)
 add_subdirectory(goffice)
 add_subdirectory(guile-json)
-add_subdirectory(gwengui-gtk3)
+if (WITH_GNUCASH)
+  add_subdirectory(gwengui-gtk3)
+endif (WITH_GNUCASH)
 
 set_local_dist(borrowed_DIST_local CMakeLists.txt README)
 set(borrowed_DIST ${borrowed_DIST_local} ${libc_DIST} ${guile-json_DIST} ${goffice_DIST} ${gwengui_gtk3_DIST} PARENT_SCOPE)
diff --git a/libgnucash/scm/CMakeLists.txt b/libgnucash/scm/CMakeLists.txt
index 518ecb9..021ab99 100644
--- a/libgnucash/scm/CMakeLists.txt
+++ b/libgnucash/scm/CMakeLists.txt
@@ -12,7 +12,7 @@ gnc_add_scheme_targets(scm-scm
 gnc_add_scheme_targets(price-quotes
   price-quotes.scm
   gnucash
-  "scm-scm;scm-gnome-utils;scm-app-utils"
+  "scm-scm;scm-app-utils"
   FALSE)
 
 
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 24e50aa..85fb0c0 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -450,6 +450,7 @@ gnucash/report/report-system/eguile-utilities.scm
 gnucash/report/report-system/gncmod-report-system.c
 gnucash/report/report-system/gnc-report.c
 gnucash/report/report-system/html-acct-table.scm
+gnucash/report/report-system/html-anytag.scm
 gnucash/report/report-system/html-barchart.scm
 gnucash/report/report-system/html-document.scm
 gnucash/report/report-system/html-fonts.scm

commit 690ef626a8b60cc172d7e4645fae3b5ecca449f7
Merge: 8cfa078 848e3da
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Jun 7 11:55:52 2018 -0700

    Merge Carsten Rinke's branch 'Bug787401-TestReport-Definition' into maint.


commit 8cfa078bd7b0531b83879aeea75b8e4d10577f4d
Merge: b431b64 1641c42
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Jun 7 10:48:28 2018 -0700

    Merge Chris Lam's 'maint-html-layout' into maint.


commit b431b648a91ca03a1891b8562c8c011a8b8df423
Merge: 0b10b4b b787baa
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Jun 7 10:10:31 2018 -0700

    Merge Chris Lam's branch 'maint-test-transaction-amendments' into maint.


commit b787baaa8f0a763cd6162e5cb9fa945e8cc36784
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Thu Jun 7 19:12:07 2018 +0800

    [test-transaction] modify test to be acceptable to MacOS
    
    This modification will convert string to number (using a customized
    function defined in same file) to obviate need to encode #pound sign
    in scheme.

diff --git a/gnucash/report/standard-reports/test/test-transaction.scm b/gnucash/report/standard-reports/test/test-transaction.scm
index 2abd884..7e4d0c7 100644
--- a/gnucash/report/standard-reports/test/test-transaction.scm
+++ b/gnucash/report/standard-reports/test/test-transaction.scm
@@ -172,7 +172,7 @@
 
     ;; Here we set foreign currencies
 
-    (gnc-commodity-set-user-symbol foreign2 "£")
+    (gnc-commodity-set-user-symbol foreign2 "#")
 
     (with-account
      gbp-bank
@@ -250,9 +250,9 @@
     ;; run in modern times, otherwise these transactions will be mixed
     ;; up with the old transactions above. The year end net bank balance
     ;; should be (* 12 (+ 103 109 -22)) = $2280.
-    ;; there will also be a £51 income monthly, tested at end of file
+    ;; there will also be a #51 income monthly, tested at end of file
     (for-each (lambda (m)
-                (env-transfer env 08 (1+ m) YEAR gbp-income gbp-bank 51 #:description "£51 income")
+                (env-transfer env 08 (1+ m) YEAR gbp-income gbp-bank 51 #:description "#51 income")
                 (env-transfer env 03 (1+ m) YEAR income bank  103 #:description "$103 income")
                 (env-transfer env 15 (1+ m) YEAR bank expense  22 #:description "$22 expense")
                 (env-transfer env 09 (1+ m) YEAR income bank  109 #:description "$109 income"))
@@ -585,7 +585,7 @@
         (test-equal "Other Account Name and Code displayed"
           (list "01-GBP GBP Bank")
           (get-row-col sxml 7 6))
-        (test-equal "GBP original currency totals = £4"
+        (test-equal "GBP original currency totals = #4"
           (list 4.0)
           (map str->num (get-row-col sxml 5 10)))
         (test-assert "USD original currency totals = $5"
@@ -808,8 +808,8 @@
                 "$190.00" "$190.00" "$190.00" "$190.00" "$190.00" "$190.00" "$2,280.00" "$190.00")
           (get-row-col sxml 1 #f))
         (test-equal "summary gbp bank-row is correct"
-          (list "GBP Bank" "£51.00" "£51.00" "£51.00" "£51.00" "£51.00" "£51.00"
-                "£51.00" "£51.00" "£51.00" "£51.00" "£51.00" "£51.00" "£612.00" "£51.00")
+          (list "GBP Bank" "#51.00" "#51.00" "#51.00" "#51.00" "#51.00" "#51.00"
+                "#51.00" "#51.00" "#51.00" "#51.00" "#51.00" "#51.00" "#612.00" "#51.00")
           (get-row-col sxml 2 #f))
         (test-equal "summary expense-row is correct"
           (list "Expenses" "$22.00" "$22.00" "$22.00" "$22.00" "$22.00" "$22.00"
@@ -821,11 +821,11 @@
                 "-$212.00" "-$2,544.00" "-$212.00")
           (get-row-col sxml 4 #f))
         (test-equal "summary gbp income-row is correct"
-          (list "Income-GBP" "-£51.00" "-£51.00" "-£51.00" "-£51.00" "-£51.00" "-£51.00"
-                "-£51.00" "-£51.00" "-£51.00" "-£51.00" "-£51.00" "-£51.00" "-£612.00" "-£51.00")
+          (list "Income-GBP" "-#51.00" "-#51.00" "-#51.00" "-#51.00" "-#51.00" "-#51.00"
+                "-#51.00" "-#51.00" "-#51.00" "-#51.00" "-#51.00" "-#51.00" "-#612.00" "-#51.00")
           (get-row-col sxml 5 #f))
         (test-equal "summary gbp total-row is correct"
-          (list "Grand Total" "£0.00" "£0.00")
+          (list "Grand Total" "#0.00" "#0.00")
           (get-row-col sxml 6 #f))
         (test-equal "summary total-row is correct"
           (list "$0.00" "$0.00")

commit 1641c422478c1e66acfe268edbbfbde72b2f404f
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Tue Jun 5 19:47:22 2018 +0800

    [html-anytag] generic html tag object
    
    Generic tag HTML-object.
    Also defines (gnc:make-html-div/markup) (gnc:make-html-span/markup)
    
    any html object can be enclosed with (gnc:make-html-span obj)
    or (gnc:make-html-span/markup "class" obj), (gnc:make-html-div obj)
    or (gnc:make-html-div/markup "class" obj)

diff --git a/gnucash/report/report-system/CMakeLists.txt b/gnucash/report/report-system/CMakeLists.txt
index d1887b4..a06f4c4 100644
--- a/gnucash/report/report-system/CMakeLists.txt
+++ b/gnucash/report/report-system/CMakeLists.txt
@@ -69,6 +69,7 @@ set (report_system_SCHEME_3
     html-linechart.scm
     html-style-info.scm
     html-style-sheet.scm
+    html-anytag.scm
     html-table.scm
     html-text.scm
     html-utilities.scm
diff --git a/gnucash/report/report-system/html-anytag.scm b/gnucash/report/report-system/html-anytag.scm
new file mode 100644
index 0000000..879fe1d
--- /dev/null
+++ b/gnucash/report/report-system/html-anytag.scm
@@ -0,0 +1,121 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; html-anytag.scm : generate HTML programmatically, with support
+;; for simple style elements. 
+;; Copyright 2018 Christopher Lam
+;;
+;; This module allows any html tag to be created such as
+;; <div> <span>
+;; 
+;; This program is free software; you can redistribute it and/or    
+;; modify it under the terms of the GNU General Public License as   
+;; published by the Free Software Foundation; either version 2 of   
+;; the License, or (at your option) any later version.              
+;;                                                                  
+;; This program is distributed in the hope that it will be useful,  
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of   
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    
+;; GNU General Public License for more details.                     
+;;                                                                  
+;; You should have received a copy of the GNU General Public License
+;; along with this program; if not, contact:
+;;
+;; Free Software Foundation           Voice:  +1-617-542-5942
+;; 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+;; Boston, MA  02110-1301,  USA       gnu at gnu.org
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(define <html-anytag>
+  (make-record-type "<html-anytag>"
+                    '(tag
+                      data
+                      style
+                      )))
+
+(define gnc:html-anytag?
+  (record-predicate <html-anytag>))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;  <html-anytag> class
+;;  wrapper around HTML anytags
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(define gnc:make-html-anytag-internal
+  (record-constructor <html-anytag>))
+
+(define (gnc:make-html-anytag tag . data)
+  (gnc:make-html-anytag-internal
+   tag                         ;; tag
+   data                        ;; data
+   (gnc:make-html-style-table) ;; style
+   ))
+
+(define gnc:html-anytag-tag
+  (record-accessor <html-anytag> 'tag))
+
+(define gnc:html-anytag-set-tag!
+  (record-modifier <html-anytag> 'tag))
+
+(define gnc:html-anytag-data
+  (record-accessor <html-anytag> 'data))
+
+(define gnc:html-anytag-set-data!
+  (record-modifier <html-anytag> 'data))
+
+(define gnc:html-anytag-style
+  (record-accessor <html-anytag> 'style))
+
+(define gnc:html-anytag-set-style-internal!
+  (record-modifier <html-anytag> 'style))
+
+(define (gnc:html-anytag-append-data! anytag . data)
+  (gnc:html-anytag-set-data!
+   anytag (append (gnc:html-anytag-data anytag) data)))
+
+(define (gnc:html-anytag-set-style! anytag tag . rest)
+  (let ((style (gnc:html-anytag-style anytag))
+        (newstyle (if (and (= (length rest) 2)
+                           (procedure? (car rest)))
+                      (apply gnc:make-html-data-style-info rest)
+                      (apply gnc:make-html-markup-style-info rest))))
+    (gnc:html-style-table-set! style tag newstyle)))
+
+(define (gnc:html-anytag-render anytag doc)
+  (let* ((tag (gnc:html-anytag-tag anytag))
+         (retval '())
+         (push (lambda (l) (set! retval (cons l retval)))))
+    ;; compile the anytag style to make other compiles faster
+    (gnc:html-style-table-compile
+     (gnc:html-anytag-style anytag) (gnc:html-document-style-stack doc))
+
+    (gnc:html-document-push-style doc (gnc:html-anytag-style anytag))
+
+    (push (gnc:html-document-markup-start doc tag #t))
+    (let ((data (gnc:html-anytag-data anytag)))
+      (for-each
+       (lambda (datum) (push (gnc:html-object-render datum doc)))
+       (if (list? data)
+           data
+           (list data))))
+    (push (gnc:html-document-markup-end doc tag))
+
+    (gnc:html-document-pop-style doc)
+
+    retval))
+
+(define (gnc:make-html-div/markup class . data)
+  (let ((anytag (apply gnc:make-html-anytag "div" data)))
+    (gnc:html-anytag-set-style! anytag "div"
+                                'attribute (list "class" class))
+    anytag))
+
+(define (gnc:make-html-span/markup class . data)
+  (let ((anytag (apply gnc:make-html-anytag "span" data)))
+    (gnc:html-anytag-set-style! anytag "span"
+                                'attribute (list "class" class))
+    anytag))
+
+(define (gnc:make-html-div . data)                 ;ideally should have been (gnc:make-html-anytag "div" data) but it will inherit parent div class.
+  (apply gnc:make-html-div/markup (cons "" data))) ;so we have to redo as an empty-string. so annoying!
+
+(define (gnc:make-html-span . data)
+  (apply gnc:make-html-span/markup (cons "" data)))
diff --git a/gnucash/report/report-system/html-document.scm b/gnucash/report/report-system/html-document.scm
index 8325da6..6978ac0 100644
--- a/gnucash/report/report-system/html-document.scm
+++ b/gnucash/report/report-system/html-document.scm
@@ -399,6 +399,9 @@
          ((gnc:html-table? obj)
           (set! o (gnc:make-html-object-internal
                    gnc:html-table-render obj)))
+         ((gnc:html-anytag? obj)
+          (set! o (gnc:make-html-object-internal
+                   gnc:html-anytag-render obj)))
          ((gnc:html-table-cell? obj)
           (set! o (gnc:make-html-object-internal
                    gnc:html-table-cell-render obj)))
diff --git a/gnucash/report/report-system/report-system.scm b/gnucash/report/report-system/report-system.scm
index 70943f2..73357a6 100644
--- a/gnucash/report/report-system/report-system.scm
+++ b/gnucash/report/report-system/report-system.scm
@@ -622,6 +622,20 @@
 (export gnc:html-table-prepend-column!)
 (export gnc:html-table-render)
 
+;; html-anytag.scm
+(export <html-anytag>)
+(export html-anytag?)
+(export gnc:html-anytag-data)
+(export gnc:html-anytag-set-data!)
+(export gnc:html-anytag-style)
+(export gnc:html-anytag-append-data!)
+(export gnc:html-anytag-set-style!)
+(export gnc:html-anytag-render div doc)
+(export gnc:make-html-div)
+(export gnc:make-html-div/markup)
+(export gnc:make-html-span)
+(export gnc:make-html-span/markup)
+
 ;; html-text.scm
 
 (export <html-text>)
@@ -734,6 +748,7 @@
 (load-from-path "html-fonts")
 
 (load-from-path "html-style-sheet")
+(load-from-path "html-anytag")
 (load-from-path "html-table")
 (load-from-path "html-text")
 (load-from-path "html-acct-table")

commit ccf3ebda1697398656e346d5753c8efd0e31d9f1
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Jun 3 19:35:24 2018 +0800

    [STYLESHEETS] modify stylesheet to accept document style-text
    
    This commit will modify stylesheet-*.scm to pass the *document*
    style-text as well as the *style-sheet* style-text to the <style> tag.

diff --git a/gnucash/report/report-system/html-fonts.scm b/gnucash/report/report-system/html-fonts.scm
index fee9af9..d87ba13 100644
--- a/gnucash/report/report-system/html-fonts.scm
+++ b/gnucash/report/report-system/html-fonts.scm
@@ -132,7 +132,7 @@
 )
 
 ;; Adds CSS style information to an html document
-(define (add-css-information-to-doc options ssdoc)
+(define (add-css-information-to-doc options ssdoc doc)
     (let*
         ((opt-val 
             (lambda (section name)
@@ -173,6 +173,7 @@
                 "td.total-number-cell { " total-number-cell-font-info " }\n"
                 "td.total-label-cell { " total-label-cell-font-info " }\n"
                 "td.centered-label-cell { text-align: center; " centered-label-cell-font-info " }\n"
+                (or (gnc:html-document-style-text doc) "")
             )
         )
     )
diff --git a/gnucash/report/stylesheets/stylesheet-easy.scm b/gnucash/report/stylesheets/stylesheet-easy.scm
index c380acd..3f5cff5 100644
--- a/gnucash/report/stylesheets/stylesheet-easy.scm
+++ b/gnucash/report/stylesheets/stylesheet-easy.scm
@@ -344,7 +344,7 @@
       (if (and logopixmap (> (string-length logopixmap) 0))
         (set! headcolumn 1))
 
-      (add-css-information-to-doc options ssdoc)
+      (add-css-information-to-doc options ssdoc doc)
 
       (let* ((title (gnc:html-document-title doc))
              (doc-headline (gnc:html-document-headline doc))
diff --git a/gnucash/report/stylesheets/stylesheet-fancy.scm b/gnucash/report/stylesheets/stylesheet-fancy.scm
index 2ea37d3..46f2bc1 100644
--- a/gnucash/report/stylesheets/stylesheet-fancy.scm
+++ b/gnucash/report/stylesheets/stylesheet-fancy.scm
@@ -325,7 +325,7 @@
 	(gnc:html-document-set-style!
 	 ssdoc "a" 'tag ""))
     
-    (add-css-information-to-doc options ssdoc)
+    (add-css-information-to-doc options ssdoc doc)
 
     (let ((t (gnc:make-html-table)))
       ;; we don't want a bevel for this table, but we don't want 
diff --git a/gnucash/report/stylesheets/stylesheet-footer.scm b/gnucash/report/stylesheets/stylesheet-footer.scm
index 37a198a..3464d58 100644
--- a/gnucash/report/stylesheets/stylesheet-footer.scm
+++ b/gnucash/report/stylesheets/stylesheet-footer.scm
@@ -343,7 +343,7 @@
     (if (not links?)
 	  (gnc:html-document-set-style! ssdoc "a" 'tag ""))
     
-    (add-css-information-to-doc options ssdoc)
+    (add-css-information-to-doc options ssdoc doc)
 
     (let ((t (gnc:make-html-table)))
       ;; we don't want a bevel for this table, but we don't want 
diff --git a/gnucash/report/stylesheets/stylesheet-head-or-tail.scm b/gnucash/report/stylesheets/stylesheet-head-or-tail.scm
index f6a279c..c2cbfd4 100644
--- a/gnucash/report/stylesheets/stylesheet-head-or-tail.scm
+++ b/gnucash/report/stylesheets/stylesheet-head-or-tail.scm
@@ -408,7 +408,7 @@
     (if (not links?)
 	  (gnc:html-document-set-style! ssdoc "a" 'tag ""))
 
-    (add-css-information-to-doc options ssdoc)
+    (add-css-information-to-doc options ssdoc doc)
 
     (let ((t (gnc:make-html-table)))
       ;; we don't want a bevel for this table, but we don't want
diff --git a/gnucash/report/stylesheets/stylesheet-plain.scm b/gnucash/report/stylesheets/stylesheet-plain.scm
index 511f00d..2a9d7e3 100644
--- a/gnucash/report/stylesheets/stylesheet-plain.scm
+++ b/gnucash/report/stylesheets/stylesheet-plain.scm
@@ -216,7 +216,7 @@
      ssdoc "a"
      'tag ""))
 
-    (add-css-information-to-doc options ssdoc)
+    (add-css-information-to-doc options ssdoc doc)
 
     (let* ((title (gnc:html-document-title doc))
            (doc-headline (gnc:html-document-headline doc))

commit da0160add8ea1007870cac9c479d22ef7517f4c5
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun May 27 16:29:06 2018 +0800

    [test-cashflow-barchart] hide debugging messages
    
    These tests pass. Don't need debugging output anymore.

diff --git a/gnucash/report/standard-reports/test/test-cashflow-barchart.scm b/gnucash/report/standard-reports/test/test-cashflow-barchart.scm
index 248468a..ecb3493 100644
--- a/gnucash/report/standard-reports/test/test-cashflow-barchart.scm
+++ b/gnucash/report/standard-reports/test/test-cashflow-barchart.scm
@@ -95,8 +95,8 @@
         (set-option report gnc:pagename-general "Price Source" 'pricedb-nearest)
         (set-option report gnc:pagename-general "Report's currency"  (gnc-default-report-currency))
         (set-option report gnc:pagename-accounts "Accounts" (list wallet-account bank-account))
-        (format #t "Create first transaction on ~a~%" (gnc-ctime date-1))
-        (format #t "Create second transaction on ~a~%" (gnc-ctime date-2))
+        ;; (format #t "Create first transaction on ~a~%" (gnc-ctime date-1))
+        ;; (format #t "Create second transaction on ~a~%" (gnc-ctime date-2))
         (let ((doc (renderer report)))
           (gnc:html-document-set-style-sheet! doc (gnc:report-stylesheet report))
           (let* ((result (gnc:html-document-render doc #f))
@@ -114,7 +114,7 @@
                                                  (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
                                                  (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1))
                                            result))))
-            (format #t "Report Result ~a~%" result)
+            ;; (format #t "Report Result ~a~%" result)
             (and (every (lambda (row)                 ; test in=net & out=0 in all rows (all days)
                           (and (or (equal? (second row) (fourth row))
                                    (begin (format #t "Failed, ~a and ~a differ~%" (second row) (fourth row)) #f))

commit 848e3da105b93b0402a51bbfcdcba8d1b9f8503d
Author: Carsten Rinke <carsten.rinke at gmx.de>
Date:   Tue Jun 5 22:01:18 2018 +0200

    Bug787401 - Test Report System - Report Definition
    
    include change proposals from responses to PR#360

diff --git a/gnucash/report/report-system/test/test-report-system.scm b/gnucash/report/report-system/test/test-report-system.scm
index 6ad049a..289ce6d 100644
--- a/gnucash/report/report-system/test/test-report-system.scm
+++ b/gnucash/report/report-system/test/test-report-system.scm
@@ -16,8 +16,8 @@
     (test-assert "Missing GUID detection" (test-check2))
     (test-assert "Detect double GUID" (test-check3))
     (test-assert "Report with Full Argument Set" (test-check4))
-    (test-end "Testing/Temporary/test-report-system")
     (set! test-report-system-flag #f)
+    (test-end "Testing/Temporary/test-report-system")
 )
 
 ;; -----------------------------------------------------------------------
@@ -29,10 +29,7 @@
 ;; -----------------------------------------------------------------------
 
 (define (test-check2)
-  (if (not (gnc:define-report 'version "1" 'name "Test Report Template"))
-    #t
-    #f
-  )
+  (not (gnc:define-report 'version "1" 'name "Test Report Template"))
 )
 
 ;; -----------------------------------------------------------------------
@@ -48,7 +45,21 @@
 
 (define (test-check4)
   (and
-    (gnc:define-report 'version "1" 'name "Test Report Template" 'report-guid "54c2fc051af64a08ba2334c2e9179e24" 'parent-type "Parent Type" 'options-generator "Options Generator" 'renderer "Renderer" 'options-cleanup-cb "Options Clean-Up" 'options-changed-cb "Options Changed" 'in-menu? #f 'menu-path "Menu Path" 'menu-name "Menu Name" 'menu-tip "Menu Tip" 'export-types "Export Types" 'export-thunk "Export Thunk")
+    (gnc:define-report 'version "1"
+                       'name "Test Report Template"
+                       'report-guid "54c2fc051af64a08ba2334c2e9179e24"
+                       'parent-type "Parent Type"
+                       'options-generator "Options Generator"
+                       'renderer "Renderer"
+                       'options-cleanup-cb "Options Clean-Up"
+                       'options-changed-cb "Options Changed"
+                       'in-menu? #f
+                       'menu-path "Menu Path"
+                       'menu-name "Menu Name"
+                       'menu-tip "Menu Tip"
+                       'export-types "Export Types"
+                       'export-thunk "Export Thunk"
+    )
     (string=? (gnc:report-template-version (gnc:find-report-template "54c2fc051af64a08ba2334c2e9179e24")) "1")
     (string=? (gnc:report-template-name (gnc:find-report-template "54c2fc051af64a08ba2334c2e9179e24")) "Test Report Template")
     (string=? (gnc:report-template-report-guid
@@ -56,13 +67,13 @@
     ;; parent type is not exported -> it is used in gnc:make-report
     (string=? (gnc:report-template-options-generator (gnc:find-report-template "54c2fc051af64a08ba2334c2e9179e24")) "Options Generator")
     (string=? (gnc:report-template-renderer (gnc:find-report-template "54c2fc051af64a08ba2334c2e9179e24")) "Renderer")
-    (string=? (gnc:report-template-options-cleanup-cb (gnc:find-report-template "54c2fc051af64a08ba2334c2e9179e24"))"Options Clean-Up")
-    (string=? (gnc:report-template-options-changed-cb (gnc:find-report-template "54c2fc051af64a08ba2334c2e9179e24"))"Options Changed")
+    (string=? (gnc:report-template-options-cleanup-cb (gnc:find-report-template "54c2fc051af64a08ba2334c2e9179e24")) "Options Clean-Up")
+    (string=? (gnc:report-template-options-changed-cb (gnc:find-report-template "54c2fc051af64a08ba2334c2e9179e24")) "Options Changed")
     (not (gnc:report-template-in-menu? (gnc:find-report-template "54c2fc051af64a08ba2334c2e9179e24")))
-    (string=? (gnc:report-template-menu-path (gnc:find-report-template "54c2fc051af64a08ba2334c2e9179e24"))"Menu Path")
-    (string=? (gnc:report-template-menu-name (gnc:find-report-template "54c2fc051af64a08ba2334c2e9179e24"))"Menu Name")
-    (string=? (gnc:report-template-menu-tip (gnc:find-report-template "54c2fc051af64a08ba2334c2e9179e24"))"Menu Tip")
-    (string=? (gnc:report-template-export-types (gnc:find-report-template "54c2fc051af64a08ba2334c2e9179e24"))"Export Types")
-    (string=? (gnc:report-template-export-thunk (gnc:find-report-template "54c2fc051af64a08ba2334c2e9179e24"))"Export Thunk")
+    (string=? (gnc:report-template-menu-path (gnc:find-report-template "54c2fc051af64a08ba2334c2e9179e24")) "Menu Path")
+    (string=? (gnc:report-template-menu-name (gnc:find-report-template "54c2fc051af64a08ba2334c2e9179e24")) "Menu Name")
+    (string=? (gnc:report-template-menu-tip (gnc:find-report-template "54c2fc051af64a08ba2334c2e9179e24")) "Menu Tip")
+    (string=? (gnc:report-template-export-types (gnc:find-report-template "54c2fc051af64a08ba2334c2e9179e24")) "Export Types")
+    (string=? (gnc:report-template-export-thunk (gnc:find-report-template "54c2fc051af64a08ba2334c2e9179e24")) "Export Thunk")
   )
 )

commit 324cfeb3a4323711529f441e2278476c03a8dcec
Author: Carsten Rinke <carsten.rinke at gmx.de>
Date:   Mon Jun 4 23:28:37 2018 +0200

    Bug787401 - Test Report System - Report Definition

diff --git a/gnucash/report/report-system/report.scm b/gnucash/report/report-system/report.scm
index 00b5a4c..39ea6ca 100644
--- a/gnucash/report/report-system/report.scm
+++ b/gnucash/report/report-system/report.scm
@@ -72,6 +72,9 @@
 (define gnc:optname-stylesheet (N_ "Stylesheet"))
 (define gnc:menuname-business-reports (N_ "_Business"))
 (define gnc:optname-invoice-number (N_ "Invoice Number"))
+(define test-report-system-flag #f)
+
+(export test-report-system-flag)
 
 ;; We want to warn users if they've got an old-style, non-guid custom
 ;; report-template, but only once
@@ -118,7 +121,7 @@
                           in-report-rec
                           (blank-report))))
       (if (null? args)
-          in-report-rec
+          report-rec
           (let ((id (car args))
                (value (cadr args))
                (remainder (cddr args)))
@@ -139,9 +142,11 @@
 		;; FIXME: We should pass the top-level window
 		;; instead of the '() to gnc-error-dialog, but I
 		;; have no idea where to get it from.
-		(gnc-error-dialog '() (string-append
+                (if (not test-report-system-flag)
+		  (gnc-error-dialog '() (string-append
           (_ "One of your reports has a report-guid that is a duplicate. Please check the report system, especially your saved reports, for a report with this report-guid: ")
           report-guid))
+                #f)
 		)))
 	(begin
 	  (if (gnc:report-template-name report-rec)
@@ -157,27 +162,39 @@
 				    (gnc:report-template-renderer report-rec))
 			    (not (gnc:report-template-parent-type rec)))
 		       (begin
-			 (gnc:debug "gnc:define-report: setting parent-type of " (gnc:report-template-name report-rec) " to " (gnc:report-template-report-guid rec))
+			 (gnc:warn "gnc:define-report: setting parent-type of " (gnc:report-template-name report-rec) " to " (gnc:report-template-report-guid rec))
 			 (gnc:report-template-set-parent-type! report-rec (gnc:report-template-report-guid rec))
 			 (gnc:debug "done setting, is now " (gnc:report-template-parent-type report-rec))))) 
 		 *gnc:_report-templates_*)
 
+                (if (gnc:report-template-parent-type report-rec)
+                  (begin
+		    ;; re-save this old-style report in the new format
+		    (gnc:report-template-save-to-savefile report-rec)
+		    (gnc:debug "complete saving " (gnc:report-template-name report-rec) " in new format")
+                    (if (not gnc:old-style-report-warned)
+	              (begin
+		        (set! gnc:old-style-report-warned #t)
+                        (if (not test-report-system-flag) ;; do not call this during "make test"
+		          (gnc-error-dialog '() (string-append (_ "The GnuCash report system has been upgraded. Your old saved reports have been transferred into a new format. If you experience trouble with saved reports, please contact the GnuCash development team."))))
+	                (hash-set! *gnc:_report-templates_* (gnc:report-template-report-guid report-rec) report-rec)
+                      )
+                    )
+                  )
+                  ;;there is no parent -> this is an inital faulty report definition
+                  (if (not test-report-system-flag) ;; do not call this during "make test"
+                    (gnc-error-dialog '() (string-append (_ "Wrong report definition: ")
+                                                            (gnc:report-template-name report-rec)
+                                                         (_ " Report is missing a GUID.")))
+                  )
+                )
+              )
+            )
+          #f ;; report definition is faulty: does not include name
 
-		;; re-save this old-style report in the new format
-		(gnc:report-template-save-to-savefile report-rec)
-		(gnc:debug "complete saving " (gnc:report-template-name report-rec) " in new format")
-		))
-
+	  ;;(gnc:warn "gnc:define-report: old-style report. setting guid for " (gnc:report-template-name report-rec) " to " (gnc:report-template-report-guid report-rec)) ;; obsolete
 
-	  
-	  (if (not gnc:old-style-report-warned)
-	      (begin
-		(set! gnc:old-style-report-warned #t)
-		(gnc-error-dialog '() (string-append (_ "The GnuCash report system has been upgraded. Your old saved reports have been transferred into a new format. If you experience trouble with saved reports, please contact the GnuCash development team.")))))
-	  (hash-set! *gnc:_report-templates_*
-		     (gnc:report-template-report-guid report-rec) report-rec)
-	  (gnc:warn "gnc:define-report: old-style report. setting guid for " (gnc:report-template-name report-rec) " to " (gnc:report-template-report-guid report-rec)))
-	)))
+	))))
 
 (define gnc:report-template-version
   (record-accessor <report-template> 'version))
diff --git a/gnucash/report/report-system/test/CMakeLists.txt b/gnucash/report/report-system/test/CMakeLists.txt
index 52ab315..7ecd67a 100644
--- a/gnucash/report/report-system/test/CMakeLists.txt
+++ b/gnucash/report/report-system/test/CMakeLists.txt
@@ -18,6 +18,7 @@ set(scm_test_report_system_SOURCES
 
 set (scm_test_report_system_with_srfi64_SOURCES
   test-html-utilities-srfi64.scm
+  test-report-system.scm
   )
 
 set(GUILE_DEPENDS
diff --git a/gnucash/report/report-system/test/test-report-system.scm b/gnucash/report/report-system/test/test-report-system.scm
new file mode 100644
index 0000000..6ad049a
--- /dev/null
+++ b/gnucash/report/report-system/test/test-report-system.scm
@@ -0,0 +1,68 @@
+(use-modules (gnucash gnc-module))
+
+(gnc:module-begin-syntax (gnc:module-load "gnucash/app-utils" 0))
+
+(use-modules (gnucash engine test test-extras))
+(use-modules (gnucash report report-system))
+(use-modules (srfi srfi-64))
+(use-modules (gnucash engine test srfi64-extras))
+
+(define (run-test)
+    (set! test-report-system-flag #t)
+    (test-runner-factory gnc:test-runner)
+    (test-begin "Testing/Temporary/test-report-system") ;; if (test-runner-factory gnc:test-runner) is commented out, this
+                                                            ;; will create Testing/Temporary/test-asset-performance.log
+    (test-assert "Minimum Report Definition" (test-check1))
+    (test-assert "Missing GUID detection" (test-check2))
+    (test-assert "Detect double GUID" (test-check3))
+    (test-assert "Report with Full Argument Set" (test-check4))
+    (test-end "Testing/Temporary/test-report-system")
+    (set! test-report-system-flag #f)
+)
+
+;; -----------------------------------------------------------------------
+
+(define (test-check1)
+  (gnc:define-report 'version "1" 'name "Test Report Template" 'report-guid "54c2fc051af64a08ba2334c2e9179e23")
+)
+
+;; -----------------------------------------------------------------------
+
+(define (test-check2)
+  (if (not (gnc:define-report 'version "1" 'name "Test Report Template"))
+    #t
+    #f
+  )
+)
+
+;; -----------------------------------------------------------------------
+
+(define (test-check3)
+  (if (not (gnc:define-report 'version "1" 'name "Test Report Template" 'report-guid "54c2fc051af64a08ba2334c2e9179e23" 'parent-type "Parent Type" 'options-generator "Options Generator" 'renderer "Renderer" 'options-cleanup-cb "Options Clean-Up" 'options-changed-cb "Options Changed" 'in-menu? #f 'menu-path "Menu Path" 'menu-name "Menu Name" 'menu-tip "Menu Tip" 'export-types "Export Types" 'export-thunk "Export Thunk"))
+    #t
+    #f
+  )
+)
+
+;; -----------------------------------------------------------------------
+
+(define (test-check4)
+  (and
+    (gnc:define-report 'version "1" 'name "Test Report Template" 'report-guid "54c2fc051af64a08ba2334c2e9179e24" 'parent-type "Parent Type" 'options-generator "Options Generator" 'renderer "Renderer" 'options-cleanup-cb "Options Clean-Up" 'options-changed-cb "Options Changed" 'in-menu? #f 'menu-path "Menu Path" 'menu-name "Menu Name" 'menu-tip "Menu Tip" 'export-types "Export Types" 'export-thunk "Export Thunk")
+    (string=? (gnc:report-template-version (gnc:find-report-template "54c2fc051af64a08ba2334c2e9179e24")) "1")
+    (string=? (gnc:report-template-name (gnc:find-report-template "54c2fc051af64a08ba2334c2e9179e24")) "Test Report Template")
+    (string=? (gnc:report-template-report-guid
+                (gnc:find-report-template "54c2fc051af64a08ba2334c2e9179e24")) "54c2fc051af64a08ba2334c2e9179e24")
+    ;; parent type is not exported -> it is used in gnc:make-report
+    (string=? (gnc:report-template-options-generator (gnc:find-report-template "54c2fc051af64a08ba2334c2e9179e24")) "Options Generator")
+    (string=? (gnc:report-template-renderer (gnc:find-report-template "54c2fc051af64a08ba2334c2e9179e24")) "Renderer")
+    (string=? (gnc:report-template-options-cleanup-cb (gnc:find-report-template "54c2fc051af64a08ba2334c2e9179e24"))"Options Clean-Up")
+    (string=? (gnc:report-template-options-changed-cb (gnc:find-report-template "54c2fc051af64a08ba2334c2e9179e24"))"Options Changed")
+    (not (gnc:report-template-in-menu? (gnc:find-report-template "54c2fc051af64a08ba2334c2e9179e24")))
+    (string=? (gnc:report-template-menu-path (gnc:find-report-template "54c2fc051af64a08ba2334c2e9179e24"))"Menu Path")
+    (string=? (gnc:report-template-menu-name (gnc:find-report-template "54c2fc051af64a08ba2334c2e9179e24"))"Menu Name")
+    (string=? (gnc:report-template-menu-tip (gnc:find-report-template "54c2fc051af64a08ba2334c2e9179e24"))"Menu Tip")
+    (string=? (gnc:report-template-export-types (gnc:find-report-template "54c2fc051af64a08ba2334c2e9179e24"))"Export Types")
+    (string=? (gnc:report-template-export-thunk (gnc:find-report-template "54c2fc051af64a08ba2334c2e9179e24"))"Export Thunk")
+  )
+)

commit 0b10b4b9ed432e8837ae1551c22956daac9ee3c2
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Jun 4 13:40:53 2018 -0700

    Bug 796423 - Cannot Input Chinese, seems does not work with...
    
    other IME too .
    
    Also Bug 795253 - Have problems input Chinese.
    
    Adds GTK_IM_MODULE_FILE to environment on Macs so that IME is enabled.
    Removes toggling the GtkEntry's editability in
    gnucash_sheet_key_press_event_internal; disabling it killed the preedit
    and that broke IME.

diff --git a/gnucash/CMakeLists.txt b/gnucash/CMakeLists.txt
index 0d4d2d9..84f764c 100644
--- a/gnucash/CMakeLists.txt
+++ b/gnucash/CMakeLists.txt
@@ -188,6 +188,7 @@ if (MAC_INTEGRATION)
   file(APPEND ${ENV_FILE_OUT} "FONTCONFIG_FILE={GNC_HOME}/etc/fonts/fonts.conf\n")
   file(APPEND ${ENV_FILE_OUT} "OFX_DTD_PATH={GNC_HOME}/share/libofx/dtd\n")
   file(APPEND ${ENV_FILE_OUT} "GNC_DBD_DIR={SYS_LIB}/dbd\n")
+  file(APPEND ${ENV_FILE_OUT} "GTK_IM_MODULE_FILE={GNC_HOME}/lib/gtk-3.0/3.0.0/immodules.cache\n")
 endif()
 
 file(COPY ${BUILD_ENV_FILE_OUT}
diff --git a/gnucash/register/register-gnome/gnucash-sheet.c b/gnucash/register/register-gnome/gnucash-sheet.c
index 2fd6398..2ba4ad6 100644
--- a/gnucash/register/register-gnome/gnucash-sheet.c
+++ b/gnucash/register/register-gnome/gnucash-sheet.c
@@ -1810,13 +1810,11 @@ gnucash_sheet_key_press_event_internal (GtkWidget *widget, GdkEventKey *event)
     if (pass_on)
     {
         gboolean result = FALSE;
-        gtk_editable_set_editable(GTK_EDITABLE(sheet->entry), TRUE);
 
         // If sheet is readonly, entry is not realized
         if (gtk_widget_get_realized (GTK_WIDGET(sheet->entry)))
             result = gtk_widget_event (sheet->entry, (GdkEvent *) event);
 
-        gtk_editable_set_editable(GTK_EDITABLE(sheet->entry), FALSE);
         return result;
     }
 

commit b4b16be30f6cf346ce580e37dbebf2c6320c38d1
Author: Keve Müller <keve.mueller at gmail.com>
Date:   Mon May 28 12:15:31 2018 +0200

    Small XML related fixes removing ambiguity improving uniformity
    
    acctchrt_common:
    there is a gratituous "i"
    
    acctchrt_full (ja):
    the Retirement and Spouse's retirement accounts have two parents
    (Investment as well as ROOT), removed ROOT.
    
    uk-vat:
    the root account was pasted a line too high 7y ago. all other files have
    the header block continuous

diff --git a/data/accounts/ca/acctchrt_common.gnucash-xea b/data/accounts/ca/acctchrt_common.gnucash-xea
index b24bfa4..9e82663 100644
--- a/data/accounts/ca/acctchrt_common.gnucash-xea
+++ b/data/accounts/ca/acctchrt_common.gnucash-xea
@@ -698,7 +698,7 @@
   <act:parent type="new">57635fa5f71dee8ffc207c277250e773</act:parent>
 </gnc:account>
 <gnc:account version="2.0.0">
-  <act:name>Recollida de brossa</act:name>i
+  <act:name>Recollida de brossa</act:name>
   <act:id type="new">2d0315d7b2f8f11a8a8b32d805bca6eb</act:id>
   <act:type>EXPENSE</act:type>
   <act:commodity>
diff --git a/data/accounts/en_GB/uk-vat.gnucash-xea b/data/accounts/en_GB/uk-vat.gnucash-xea
index 5603576..31bdb59 100644
--- a/data/accounts/en_GB/uk-vat.gnucash-xea
+++ b/data/accounts/en_GB/uk-vat.gnucash-xea
@@ -40,13 +40,13 @@
   <gnc-act:long-description>
     A basic set of accounts for tracking VAT in the UK.
   </gnc-act:long-description>
+  <gnc-act:start-selected>0</gnc-act:start-selected>
   <gnc:account version="2.0.0">
   <act:name>Root Account</act:name>
   <act:id type="new">1972cce2e2364f95b2b0bc014502661d</act:id>
   <act:type>ROOT</act:type>
   <act:commodity-scu>0</act:commodity-scu>
 </gnc:account>
-  <gnc-act:start-selected>0</gnc-act:start-selected>
 <gnc:account version="2.0.0">
   <act:name>Bank Accounts</act:name>
   <act:id type="new">48a242bf9a2d8c947cb41d96493d91da</act:id>
diff --git a/data/accounts/ja/acctchrt_full.gnucash-xea b/data/accounts/ja/acctchrt_full.gnucash-xea
index be7a7b5..535271b 100644
--- a/data/accounts/ja/acctchrt_full.gnucash-xea
+++ b/data/accounts/ja/acctchrt_full.gnucash-xea
@@ -266,7 +266,7 @@
     <cmdty:id>USD</cmdty:id>
   </act:commodity>
   <act:description>個人年金</act:description>
-  <act:parent type="new">1972cce2e2364f95b2b0bc014502661d</act:parent>
+  <act:parent type="new">0efa7e60042a8d5e4f07a2e6f76bb66d</act:parent>
   <act:slots>
     <slot>
       <slot:key>placeholder</slot:key>
@@ -277,7 +277,6 @@
       <slot:value type="string">確定拠出年金、退職金、IRA、401(k)など</slot:value>
     </slot>
   </act:slots>
-  <act:parent type="new">0efa7e60042a8d5e4f07a2e6f76bb66d</act:parent>
 </gnc:account>
 <gnc:account version="2.0.0">
   <act:name>債券</act:name>
@@ -332,7 +331,7 @@
     <cmdty:id>USD</cmdty:id>
   </act:commodity>
   <act:description>配偶者個人年金</act:description>
-  <act:parent type="new">1972cce2e2364f95b2b0bc014502661d</act:parent>
+  <act:parent type="new">0efa7e60042a8d5e4f07a2e6f76bb66d</act:parent>
   <act:slots>
     <slot>
       <slot:key>notes</slot:key>
@@ -343,7 +342,6 @@
       <slot:value type="string">true</slot:value>
     </slot>
   </act:slots>
-  <act:parent type="new">0efa7e60042a8d5e4f07a2e6f76bb66d</act:parent>
 </gnc:account>
 <gnc:account version="2.0.0">
   <act:name>債券</act:name>

commit a97f9faf919788f0021917696b97b791d2a10370
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Jun 4 13:21:53 2018 -0700

    Revert "Merge Keve Mueller's 'xea-fixes' into maint"
    
    This reverts commit eb67baba5b18dc4eaba7f29bf306e9091a1ff1d7, reversing
    changes made to 0064dafbad30d1146688e7329274cc10714919b3.
    
    Keve Mueller's xea-fixes branch was made from master, so merging it into
    maint effectively merged master onto maint, not something we want to do.

diff --git a/CMakeLists.txt b/CMakeLists.txt
index efaf0e5..b8c3998 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -12,13 +12,13 @@ enable_testing()
 
 # Version number of gnucash
 set (GNUCASH_MAJOR_VERSION 3)
-set (GNUCASH_MINOR_VERSION 900)
+set (GNUCASH_MINOR_VERSION 1)
 set (VERSION "${GNUCASH_MAJOR_VERSION}.${GNUCASH_MINOR_VERSION}")
 set (GNUCASH_LATEST_STABLE_SERIES 3.x)
 
 set (PACKAGE gnucash)
 set (PACKAGE_NAME GnuCash)
-set (PACKAGE_VERSION 3.900)
+set (PACKAGE_VERSION 3.1)
 set (PACKAGE_BUGREPORT gnucash-devel at gnucash.org)
 set (PACKAGE_TARNAME ${PACKAGE})
 set (PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}")
diff --git a/data/accounts/ca/acctchrt_common.gnucash-xea b/data/accounts/ca/acctchrt_common.gnucash-xea
index 9e82663..b24bfa4 100644
--- a/data/accounts/ca/acctchrt_common.gnucash-xea
+++ b/data/accounts/ca/acctchrt_common.gnucash-xea
@@ -698,7 +698,7 @@
   <act:parent type="new">57635fa5f71dee8ffc207c277250e773</act:parent>
 </gnc:account>
 <gnc:account version="2.0.0">
-  <act:name>Recollida de brossa</act:name>
+  <act:name>Recollida de brossa</act:name>i
   <act:id type="new">2d0315d7b2f8f11a8a8b32d805bca6eb</act:id>
   <act:type>EXPENSE</act:type>
   <act:commodity>
diff --git a/data/accounts/en_GB/uk-vat.gnucash-xea b/data/accounts/en_GB/uk-vat.gnucash-xea
index 31bdb59..5603576 100644
--- a/data/accounts/en_GB/uk-vat.gnucash-xea
+++ b/data/accounts/en_GB/uk-vat.gnucash-xea
@@ -40,13 +40,13 @@
   <gnc-act:long-description>
     A basic set of accounts for tracking VAT in the UK.
   </gnc-act:long-description>
-  <gnc-act:start-selected>0</gnc-act:start-selected>
   <gnc:account version="2.0.0">
   <act:name>Root Account</act:name>
   <act:id type="new">1972cce2e2364f95b2b0bc014502661d</act:id>
   <act:type>ROOT</act:type>
   <act:commodity-scu>0</act:commodity-scu>
 </gnc:account>
+  <gnc-act:start-selected>0</gnc-act:start-selected>
 <gnc:account version="2.0.0">
   <act:name>Bank Accounts</act:name>
   <act:id type="new">48a242bf9a2d8c947cb41d96493d91da</act:id>
diff --git a/data/accounts/ja/acctchrt_full.gnucash-xea b/data/accounts/ja/acctchrt_full.gnucash-xea
index 535271b..be7a7b5 100644
--- a/data/accounts/ja/acctchrt_full.gnucash-xea
+++ b/data/accounts/ja/acctchrt_full.gnucash-xea
@@ -266,7 +266,7 @@
     <cmdty:id>USD</cmdty:id>
   </act:commodity>
   <act:description>個人年金</act:description>
-  <act:parent type="new">0efa7e60042a8d5e4f07a2e6f76bb66d</act:parent>
+  <act:parent type="new">1972cce2e2364f95b2b0bc014502661d</act:parent>
   <act:slots>
     <slot>
       <slot:key>placeholder</slot:key>
@@ -277,6 +277,7 @@
       <slot:value type="string">確定拠出年金、退職金、IRA、401(k)など</slot:value>
     </slot>
   </act:slots>
+  <act:parent type="new">0efa7e60042a8d5e4f07a2e6f76bb66d</act:parent>
 </gnc:account>
 <gnc:account version="2.0.0">
   <act:name>債券</act:name>
@@ -331,7 +332,7 @@
     <cmdty:id>USD</cmdty:id>
   </act:commodity>
   <act:description>配偶者個人年金</act:description>
-  <act:parent type="new">0efa7e60042a8d5e4f07a2e6f76bb66d</act:parent>
+  <act:parent type="new">1972cce2e2364f95b2b0bc014502661d</act:parent>
   <act:slots>
     <slot>
       <slot:key>notes</slot:key>
@@ -342,6 +343,7 @@
       <slot:value type="string">true</slot:value>
     </slot>
   </act:slots>
+  <act:parent type="new">0efa7e60042a8d5e4f07a2e6f76bb66d</act:parent>
 </gnc:account>
 <gnc:account version="2.0.0">
   <act:name>債券</act:name>

commit 06808469d0a1c2483f18760a7b025abd4d57fdba
Merge: aeb2531 5093a8f
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Mon Jun 4 13:44:42 2018 +0200

    Merge branch 'maint-eradicate-gtkhtml' of https://github.com/christopherlam/gnucash into maint


commit aeb2531350b296bf6a4b8215915aba58062a5cf6
Merge: eb67bab 0dc9a11
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Mon Jun 4 11:46:46 2018 +0200

    Merge branch 'maint-scheme-more-progress' of https://github.com/christopherlam/gnucash into maint


commit 5093a8fb141e4df3bf2fad3b7757047b992d1693
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Mon Jun 4 11:01:23 2018 +0800

    [webkit] eradicate gtkhtml in .scm
    
    This commit does away with anything gtkhtml related. Some variables eg
    css? must remain because they're used later on.

diff --git a/gnucash/report/business-reports/balsheet-eg.eguile.scm b/gnucash/report/business-reports/balsheet-eg.eguile.scm
index 1bcec52..c7a91f1 100644
--- a/gnucash/report/business-reports/balsheet-eg.eguile.scm
+++ b/gnucash/report/business-reports/balsheet-eg.eguile.scm
@@ -153,9 +153,6 @@
 
 </head>
 <body>
-<?scm (if (not css?) (begin ?>
-  <table border="0" cellpadding="16"><tr><td> <!-- hack for GTKHTML -->
-<?scm )) ?>
 <h3><?scm:d coyname ?></h3>
 <h2><?scm:d reportname ?> <?scm:d (qof-print-date opt-date) ?></h2>
 
@@ -314,10 +311,6 @@
 <br clear="both">
 <p><?scm:d opt-extra-notes ?>
 
-<?scm (if (not css?) (begin ?>
-  </table> <!-- hack for GTKHTML -->
-<?scm )) ?>
-
 </body>
 </html>
 
diff --git a/gnucash/report/business-reports/balsheet-eg.scm b/gnucash/report/business-reports/balsheet-eg.scm
index d5717a9..d1b5cf2 100644
--- a/gnucash/report/business-reports/balsheet-eg.scm
+++ b/gnucash/report/business-reports/balsheet-eg.scm
@@ -433,8 +433,6 @@
          ;; XXX I haven't found a way to get the book for which the report was opened here
          (coyname (or (gnc:company-info (gnc-get-current-book) gnc:*company-name*) ""))
 
-         (css? (gnc-html-engine-supports-css))
-
          (html #f))
 
     ;; end of all the lets.  time for some real code
@@ -458,8 +456,7 @@
 
     (define (foreignstyle item)
       ;; apply styling for amount in foreign currency
-      (if css?
-        (string-append "<span class=\"foreign\">" item "</span>"))
+        (string-append "<span class=\"foreign\">" item "</span>")
         (string-append "<small><i>" item "</i></small>"))
 
     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -671,13 +668,7 @@
     (set! html (eguile-file-to-string opt-template-file (the-environment)))
     (gnc:debug "balsheet-eg.scm - generated html:") (gnc:debug html)
     (gnc:report-finished)
-    (if css? ; return report as document or html, depending on version
-      html
-      (let ((document (gnc:make-html-document)))
-        (gnc:html-document-add-object! document html)
-        document))
-
-    ))
+    html))
 
 (gnc:define-report
   'version 1
diff --git a/gnucash/report/business-reports/receipt.scm b/gnucash/report/business-reports/receipt.scm
index 6ee7250..9fb8614 100644
--- a/gnucash/report/business-reports/receipt.scm
+++ b/gnucash/report/business-reports/receipt.scm
@@ -240,22 +240,14 @@
          (opt-amount-due-heading    (opt-value headingpage2 optname-amount-due))
          (opt-payment-recd-heading  (opt-value headingpage2 optname-payment-recd))
          (opt-extra-notes           (opt-value notespage    optname-extra-notes))
-         (css? #t) ;(and (defined? 'gnc-html-engine-supports-css) (gnc-html-engine-supports-css)))
-         (html #f))
-
-    (set! html (eguile-file-to-string
+         (css? #t)
+         (html (eguile-file-to-string
                  opt-template-file
-                 (the-environment)))
+                 (the-environment))))
 
-    (gnc:debug "receipt.scm: css? is " css?)
-    (gnc:debug "receipt.scm: defined is " (defined? 'gnc-html-engine-supports-css))
     (gnc:debug "receipt.scm - generated html:") (gnc:debug html)
 
-    (if css? ; return report as document or html, depending on version
-      html
-      (let ((document (gnc:make-html-document)))
-        (gnc:html-document-add-object! document html)
-        document))))
+    html))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;; Define the report
diff --git a/gnucash/report/business-reports/taxinvoice.scm b/gnucash/report/business-reports/taxinvoice.scm
index 054b18f..fe94e64 100644
--- a/gnucash/report/business-reports/taxinvoice.scm
+++ b/gnucash/report/business-reports/taxinvoice.scm
@@ -306,22 +306,14 @@
          (opt-jobname-text          (opt-value headingpage2 optname-jobname-text))
          (opt-extra-css             (opt-value notespage    optname-extra-css)) 
          (opt-extra-notes           (opt-value notespage    optname-extra-notes)) 
-         (css? #t) ;(and (defined? 'gnc-html-engine-supports-css) (gnc-html-engine-supports-css)))
-         (html #f))
-
-    (set! html (eguile-file-to-string 
+         (css? #t)
+         (html (eguile-file-to-string
                  opt-template-file
-                 (the-environment)))
+                 (the-environment))))
 
-    (gnc:debug "taxinvoice.scm: css? is " css?)
-    (gnc:debug "taxinvoice.scm: defined is " (defined? 'gnc-html-engine-supports-css))
     (gnc:debug "taxinvoice.scm - generated html:") (gnc:debug html)
 
-    (if css? ; return report as document or html, depending on version 
-      html 
-      (let ((document (gnc:make-html-document)))
-        (gnc:html-document-add-object! document html)
-        document))))
+    html))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;; Define the report
diff --git a/gnucash/report/locale-specific/us/taxtxf.scm b/gnucash/report/locale-specific/us/taxtxf.scm
index 76325de..f8d0818 100644
--- a/gnucash/report/locale-specific/us/taxtxf.scm
+++ b/gnucash/report/locale-specific/us/taxtxf.scm
@@ -104,7 +104,7 @@
 
 (use-modules (gnucash core-utils)) ; for gnc:version
 
-(gnc:module-load "gnucash/html" 0)   ; added for 'gnc-html-engine-supports-css'
+(gnc:module-load "gnucash/html" 0)
 (gnc:module-load "gnucash/tax/us" 0)
 (gnc:module-load "gnucash/report/report-system" 0)
 
@@ -262,13 +262,7 @@
            (list 'conv-to-report-date (N_ "Nearest report date") (N_ "Use nearest to report date.")))
     )))
 
-  (if (gnc-html-engine-supports-css)
-      #t
-      (gnc:register-tax-option
-       (gnc:make-simple-boolean-option
-        gnc:pagename-display (N_ "Shade alternate transactions")
-        "n" (N_ "Shade background of alternate transactions, if more than one displayed.") #f))
-  )
+  #t
 
   (gnc:options-set-default-section options gnc:pagename-general)
 
@@ -2063,10 +2057,7 @@
                                      "Do not print Action:Memo data")
                                     (get-option gnc:pagename-display
                                      "Do not print T-Num:Memo data")))
-         (shade-alternate-transactions? (if (gnc-html-engine-supports-css)
-                                            #t
-                                            (get-option gnc:pagename-display
-                                               "Shade alternate transactions")))
+         (shade-alternate-transactions? #t)
          (currency-conversion-date (get-option gnc:pagename-display
                                  "Currency conversion date"))
          (user-sel-accnts (get-option gnc:pagename-accounts
@@ -3058,7 +3049,7 @@
               #f) ;;end of if
           #f) ;;end of if
           (begin  ; else do tax report
-             (if (gnc-html-engine-supports-css)
+             (if #t                     ;does gcn-html-engine-support-css? #t!
                  (begin ;; this is for webkit
                   (gnc:html-document-set-style!
                    doc "header-just-top"
@@ -3101,65 +3092,6 @@
                    'tag "td"
                    'attribute (list "class" "number-cell neg")
                    'attribute (list "valign" "bottom"))
-                 )
-                 (begin ;; this is for gtkhtml
-                  (gnc:html-document-set-style!
-                   doc "header-just-top"
-                   'tag "th"
-                   'attribute (list "align" "left")
-                   'attribute (list "valign" "top"))
-
-                  (gnc:html-document-set-style!
-                   doc "header-just-bot"
-                   'tag "th"
-                   'attribute (list "align" "left")
-                   'attribute (list "valign" "bottom"))
-
-                  (gnc:html-document-set-style!
-                   doc "column-heading-center"
-                   'tag "th"
-                   'attribute (list "align" "center")
-                   'attribute (list "valign" "bottom"))
-
-                  (gnc:html-document-set-style!
-                   doc "tran-detail"
-                   'tag "tr"
-                   'attribute (list "valign" "top"))
-
-                  (gnc:html-document-set-style!
-                   doc "tran-detail-shade"
-                   'tag "tr"
-                   'attribute (list "valign" "top")
-                   'attribute (list "bgcolor" "grey"))
-
-                  (gnc:html-document-set-style!
-                   doc "column-heading-right"
-                   'tag "th"
-                   'attribute (list "align" "right"))
-
-                  (gnc:html-document-set-style!
-                   doc "text-cell-center"
-                   'tag "td"
-                   'attribute (list "align" "center"))
-
-                  (gnc:html-document-set-style!
-                   doc "number-cell-bot"
-                   'tag "td"
-                   'attribute (list "align" "right")
-                   'attribute (list "nowrap" "nowrap")
-                   'attribute (list "valign" "bottom"))
-
-                  (gnc:html-document-set-style!
-                   doc "number-cell-bot-neg"
-                   'tag "td"
-                   'attribute (list "align" "right")
-                   'attribute (list "nowrap" "nowrap")
-                   'attribute (list "valign" "bottom"))
-
-                  (gnc:html-document-set-style!
-                   doc "date-cell"
-                   'tag "td"
-                   'attribute (list "nowrap" "nowrap"))
                  ))
 
              (gnc:html-document-set-style!
@@ -3469,10 +3401,7 @@
                              ;; currency conversion date
                              "      ~a <br/>"
                              ;; alternate transaction shading
-                             (if (gnc-html-engine-supports-css)
-                                 ""
-                                 "      ~a <br/>"
-                             ))
+                             "")
                              (if (not (null? user-sel-accnts))
                                  "Subset of accounts"
                                  "No accounts (none = all accounts)")
@@ -3501,11 +3430,6 @@
                                          'conv-to-tran-date)
                                  "PriceDB lookups nearest to transaction date"
                                  "PriceDB lookups nearest to report end date")
-                             (if (not (gnc-html-engine-supports-css))
-                                 (if shade-alternate-transactions?
-                                     "Shade alternate transactions"
-                                     "Do not shade alternate transactions")
-                             )
                           )
                         ))))
 
diff --git a/gnucash/report/report-system/html-document.scm b/gnucash/report/report-system/html-document.scm
index e9c77e8..8325da6 100644
--- a/gnucash/report/report-system/html-document.scm
+++ b/gnucash/report/report-system/html-document.scm
@@ -140,7 +140,6 @@
                (push (lambda (l) (set! retval (cons l retval))))
                (objs (gnc:html-document-objects doc))
                (work-to-do (length objs))
-               (css? (gnc-html-engine-supports-css))
                (work-done 0)
                (title (gnc:html-document-title doc)))
           ;; compile the doc style
@@ -160,9 +159,8 @@
                 (push "<html>\n")
                 (push "<head>\n")
                 (push "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\" />\n")
-                (if css?
-                  (if style-text
-                    (push (list "</style>" style-text "<style type=\"text/css\">\n"))))
+                (if style-text
+                    (push (list "</style>" style-text "<style type=\"text/css\">\n")))
                 (let ((title (gnc:html-document-title doc)))
                   (if title
                       (push (list "</title>" title "<title>\n"))))
diff --git a/gnucash/report/stylesheets/stylesheet-easy.scm b/gnucash/report/stylesheets/stylesheet-easy.scm
index 8f6f9b2..c380acd 100644
--- a/gnucash/report/stylesheets/stylesheet-easy.scm
+++ b/gnucash/report/stylesheets/stylesheet-easy.scm
@@ -33,7 +33,7 @@
 (use-modules (gnucash gnc-module))
 (use-modules (gnucash gettext))
 
-(gnc:module-load "gnucash/html" 0)   ; added for 'gnc-html-engine-supports-css'
+(gnc:module-load "gnucash/html" 0)
 (gnc:module-load "gnucash/report/report-system" 0)
 
 (define (easy-options)
@@ -227,143 +227,70 @@
 ;;;;
 ;;;;
 ;;;;
-    (if (gnc-html-engine-supports-css)
-        (begin ;; this is for webkit
-          (gnc:html-document-set-style!
-             ssdoc "column-heading-left"
-             'tag "th"
-             'attribute (list "class" "column-heading-left"))
-
-          (gnc:html-document-set-style!
-             ssdoc "column-heading-center"
-             'tag "th"
-             'attribute (list "class" "column-heading-center"))
-
-          (gnc:html-document-set-style!
-             ssdoc "column-heading-right"
-             'tag "th"
-             'attribute (list "class" "column-heading-right"))
-
-          (gnc:html-document-set-style!
-             ssdoc "date-cell"
-             'tag "td"
-             'attribute (list "class" "date-cell"))
-
-          (gnc:html-document-set-style!
-             ssdoc "anchor-cell"
-             'tag "td"
-             'attribute (list "class" "anchor-cell"))
-
-          (gnc:html-document-set-style!
-             ssdoc "number-cell"
-             'tag "td"
-             'attribute (list "class" "number-cell"))
-
-          (gnc:html-document-set-style!
-             ssdoc "number-cell-neg"
-             'tag "td"
-             'attribute (list "class" "number-cell neg"))
-
-          (gnc:html-document-set-style!
-             ssdoc "number-header"
-             'tag "th"
-             'attribute (list "class" "number-header"))
-
-          (gnc:html-document-set-style!
-             ssdoc "text-cell"
-             'tag "td"
-             'attribute (list "class" "text-cell"))
-
-          (gnc:html-document-set-style!
-             ssdoc "total-number-cell"
-             'tag '("td")
-             'attribute (list "class" "total-number-cell"))
-
-          (gnc:html-document-set-style!
-             ssdoc "total-number-cell-neg"
-             'tag '("td")
-             'attribute (list "class" "total-number-cell neg"))
-
-          (gnc:html-document-set-style!
-             ssdoc "total-label-cell"
-             'tag '("td")
-             'attribute (list "class" "total-label-cell"))
-
-          (gnc:html-document-set-style!
-             ssdoc "centered-label-cell"
-             'tag '("td")
-             'attribute (list "class" "centered-label-cell"))
-        )
-        (begin ;; this is for gtkhtml
-          (gnc:html-document-set-style!
-             ssdoc "column-heading-left"
-             'tag "th"
-             'attribute (list "align" "left"))
-
-          (gnc:html-document-set-style!
-             ssdoc "column-heading-center"
-             'tag "th"
-             'attribute (list "align" "center"))
-
-          (gnc:html-document-set-style!
-             ssdoc "column-heading-right"
-             'tag "th"
-             'attribute (list "align" "right"))
-
-          (gnc:html-document-set-style!
-             ssdoc "date-cell"
-             'tag "td"
-             'attribute (list "nowrap" "nowrap"))
-
-          (gnc:html-document-set-style!
-             ssdoc "anchor-cell"
-             'tag "td"
-             'attribute (list "align" "left")
-             'attribute (list "nowrap"))
-
-          (gnc:html-document-set-style!
-             ssdoc "number-cell"
-             'tag "td"
-             'attribute (list "align" "right")
-             'attribute (list "nowrap"))
-
-          (gnc:html-document-set-style!
-             ssdoc "number-cell-neg"
-             'tag "td"
-             'attribute (list "align" "right")
-             'attribute (list "nowrap"))
-
-          (gnc:html-document-set-style!
-             ssdoc "number-header"
-             'tag "th"
-             'attribute (list "align" "right"))
-
-          (gnc:html-document-set-style!
-             ssdoc "text-cell"
-             'tag "td"
-             'attribute (list "align" "left"))
-
-          (gnc:html-document-set-style!
-             ssdoc "total-number-cell"
-             'tag '("td")
-             'attribute (list "align" "right"))
-
-          (gnc:html-document-set-style!
-             ssdoc "total-number-cell-neg"
-             'tag '("td")
-             'attribute (list "align" "right"))
-
-          (gnc:html-document-set-style!
-             ssdoc "total-label-cell"
-             'tag '("td")
-             'attribute (list "align" "left"))
-
-          (gnc:html-document-set-style!
-             ssdoc "centered-label-cell"
-             'tag '("td")
-             'attribute (list "align" "center"))
-        )
-    )
+    (gnc:html-document-set-style!
+     ssdoc "column-heading-left"
+     'tag "th"
+     'attribute (list "class" "column-heading-left"))
+
+    (gnc:html-document-set-style!
+     ssdoc "column-heading-center"
+     'tag "th"
+     'attribute (list "class" "column-heading-center"))
+
+    (gnc:html-document-set-style!
+     ssdoc "column-heading-right"
+     'tag "th"
+     'attribute (list "class" "column-heading-right"))
+
+    (gnc:html-document-set-style!
+     ssdoc "date-cell"
+     'tag "td"
+     'attribute (list "class" "date-cell"))
+
+    (gnc:html-document-set-style!
+     ssdoc "anchor-cell"
+     'tag "td"
+     'attribute (list "class" "anchor-cell"))
+
+    (gnc:html-document-set-style!
+     ssdoc "number-cell"
+     'tag "td"
+     'attribute (list "class" "number-cell"))
+
+    (gnc:html-document-set-style!
+     ssdoc "number-cell-neg"
+     'tag "td"
+     'attribute (list "class" "number-cell neg"))
+
+    (gnc:html-document-set-style!
+     ssdoc "number-header"
+     'tag "th"
+     'attribute (list "class" "number-header"))
+
+    (gnc:html-document-set-style!
+     ssdoc "text-cell"
+     'tag "td"
+     'attribute (list "class" "text-cell"))
+
+    (gnc:html-document-set-style!
+     ssdoc "total-number-cell"
+     'tag '("td")
+     'attribute (list "class" "total-number-cell"))
+
+    (gnc:html-document-set-style!
+     ssdoc "total-number-cell-neg"
+     'tag '("td")
+     'attribute (list "class" "total-number-cell neg"))
+
+    (gnc:html-document-set-style!
+     ssdoc "total-label-cell"
+     'tag '("td")
+     'attribute (list "class" "total-label-cell"))
+
+    (gnc:html-document-set-style!
+     ssdoc "centered-label-cell"
+     'tag '("td")
+     'attribute (list "class" "centered-label-cell"))
 
     (if (and bgpixmap
 	     (not (string=? bgpixmap "")))
diff --git a/gnucash/report/stylesheets/stylesheet-fancy.scm b/gnucash/report/stylesheets/stylesheet-fancy.scm
index 902e21c..2ea37d3 100644
--- a/gnucash/report/stylesheets/stylesheet-fancy.scm
+++ b/gnucash/report/stylesheets/stylesheet-fancy.scm
@@ -27,7 +27,7 @@
 (use-modules (gnucash gnc-module))
 (use-modules (gnucash gettext))
 
-(gnc:module-load "gnucash/html" 0)   ; added for 'gnc-html-engine-supports-css'
+(gnc:module-load "gnucash/html" 0)
 (gnc:module-load "gnucash/report/report-system" 0)
 
 (define (fancy-options)
@@ -222,143 +222,70 @@
 ;;;;
 ;;;;
 ;;;;
-    (if (gnc-html-engine-supports-css)
-        (begin ;; this is for webkit
-          (gnc:html-document-set-style!
-             ssdoc "column-heading-left"
-             'tag "th"
-             'attribute (list "class" "column-heading-left"))
-
-          (gnc:html-document-set-style!
-             ssdoc "column-heading-center"
-             'tag "th"
-             'attribute (list "class" "column-heading-center"))
-
-          (gnc:html-document-set-style!
-             ssdoc "column-heading-right"
-             'tag "th"
-             'attribute (list "class" "column-heading-right"))
-
-          (gnc:html-document-set-style!
-             ssdoc "date-cell"
-             'tag "td"
-             'attribute (list "class" "date-cell"))
-
-          (gnc:html-document-set-style!
-             ssdoc "anchor-cell"
-             'tag "td"
-             'attribute (list "class" "anchor-cell"))
-
-          (gnc:html-document-set-style!
-             ssdoc "number-cell"
-             'tag "td"
-             'attribute (list "class" "number-cell"))
-
-          (gnc:html-document-set-style!
-             ssdoc "number-cell-neg"
-             'tag "td"
-             'attribute (list "class" "number-cell neg"))
-
-          (gnc:html-document-set-style!
-             ssdoc "number-header"
-             'tag "th"
-             'attribute (list "class" "number-header"))
-
-          (gnc:html-document-set-style!
-             ssdoc "text-cell"
-             'tag "td"
-             'attribute (list "class" "text-cell"))
-
-          (gnc:html-document-set-style!
-             ssdoc "total-number-cell"
-             'tag '("td")
-             'attribute (list "class" "total-number-cell"))
-
-          (gnc:html-document-set-style!
-             ssdoc "total-number-cell-neg"
-             'tag '("td")
-             'attribute (list "class" "total-number-cell neg"))
-
-          (gnc:html-document-set-style!
-             ssdoc "total-label-cell"
-             'tag '("td")
-             'attribute (list "class" "total-label-cell"))
-
-          (gnc:html-document-set-style!
-             ssdoc "centered-label-cell"
-             'tag '("td")
-             'attribute (list "class" "centered-label-cell"))
-        )
-        (begin ;; this is for gtkhtml
-          (gnc:html-document-set-style!
-             ssdoc "column-heading-left"
-             'tag "th"
-             'attribute (list "align" "left"))
-
-          (gnc:html-document-set-style!
-             ssdoc "column-heading-center"
-             'tag "th"
-             'attribute (list "align" "center"))
-
-          (gnc:html-document-set-style!
-             ssdoc "column-heading-right"
-             'tag "th"
-             'attribute (list "align" "right"))
-
-          (gnc:html-document-set-style!
-             ssdoc "date-cell"
-             'tag "td"
-             'attribute (list "nowrap" "nowrap"))
-
-          (gnc:html-document-set-style!
-             ssdoc "anchor-cell"
-             'tag "td"
-             'attribute (list "align" "left")
-             'attribute (list "nowrap"))
-
-          (gnc:html-document-set-style!
-             ssdoc "number-cell"
-             'tag "td"
-             'attribute (list "align" "right")
-             'attribute (list "nowrap"))
-
-          (gnc:html-document-set-style!
-             ssdoc "number-cell-neg"
-             'tag "td"
-             'attribute (list "align" "right")
-             'attribute (list "nowrap"))
-
-          (gnc:html-document-set-style!
-             ssdoc "number-header"
-             'tag "th"
-             'attribute (list "align" "right"))
-
-          (gnc:html-document-set-style!
-             ssdoc "text-cell"
-             'tag "td"
-             'attribute (list "align" "left"))
-
-          (gnc:html-document-set-style!
-             ssdoc "total-number-cell"
-             'tag '("td")
-             'attribute (list "align" "right"))
-
-          (gnc:html-document-set-style!
-             ssdoc "total-number-cell-neg"
-             'tag '("td")
-             'attribute (list "align" "right"))
-
-          (gnc:html-document-set-style!
-             ssdoc "total-label-cell"
-             'tag '("td")
-             'attribute (list "align" "left"))
-
-          (gnc:html-document-set-style!
-             ssdoc "centered-label-cell"
-             'tag '("td")
-             'attribute (list "align" "center"))
-        )
-    )
+    (gnc:html-document-set-style!
+     ssdoc "column-heading-left"
+     'tag "th"
+     'attribute (list "class" "column-heading-left"))
+
+    (gnc:html-document-set-style!
+     ssdoc "column-heading-center"
+     'tag "th"
+     'attribute (list "class" "column-heading-center"))
+
+    (gnc:html-document-set-style!
+     ssdoc "column-heading-right"
+     'tag "th"
+     'attribute (list "class" "column-heading-right"))
+
+    (gnc:html-document-set-style!
+     ssdoc "date-cell"
+     'tag "td"
+     'attribute (list "class" "date-cell"))
+
+    (gnc:html-document-set-style!
+     ssdoc "anchor-cell"
+     'tag "td"
+     'attribute (list "class" "anchor-cell"))
+
+    (gnc:html-document-set-style!
+     ssdoc "number-cell"
+     'tag "td"
+     'attribute (list "class" "number-cell"))
+
+    (gnc:html-document-set-style!
+     ssdoc "number-cell-neg"
+     'tag "td"
+     'attribute (list "class" "number-cell neg"))
+
+    (gnc:html-document-set-style!
+     ssdoc "number-header"
+     'tag "th"
+     'attribute (list "class" "number-header"))
+
+    (gnc:html-document-set-style!
+     ssdoc "text-cell"
+     'tag "td"
+     'attribute (list "class" "text-cell"))
+
+    (gnc:html-document-set-style!
+     ssdoc "total-number-cell"
+     'tag '("td")
+     'attribute (list "class" "total-number-cell"))
+
+    (gnc:html-document-set-style!
+     ssdoc "total-number-cell-neg"
+     'tag '("td")
+     'attribute (list "class" "total-number-cell neg"))
+
+    (gnc:html-document-set-style!
+     ssdoc "total-label-cell"
+     'tag '("td")
+     'attribute (list "class" "total-label-cell"))
+
+    (gnc:html-document-set-style!
+     ssdoc "centered-label-cell"
+     'tag '("td")
+     'attribute (list "class" "centered-label-cell"))
 
     (if (and bgpixmap
 	     (not (string=? bgpixmap "")))
diff --git a/gnucash/report/stylesheets/stylesheet-footer.scm b/gnucash/report/stylesheets/stylesheet-footer.scm
index afb41f1..37a198a 100644
--- a/gnucash/report/stylesheets/stylesheet-footer.scm
+++ b/gnucash/report/stylesheets/stylesheet-footer.scm
@@ -38,7 +38,7 @@
 (use-modules (gnucash gnc-module))
 (use-modules (gnucash gettext))
 
-(gnc:module-load "gnucash/html" 0)   ; added for 'gnc-html-engine-supports-css'
+(gnc:module-load "gnucash/html" 0)
 (gnc:module-load "gnucash/report/report-system" 0)
 
 (define (footer-options)
@@ -241,143 +241,70 @@
 ;;;;
 ;;;;
 ;;;;
-    (if (gnc-html-engine-supports-css)
-        (begin ;; this is for webkit
-          (gnc:html-document-set-style!
-             ssdoc "column-heading-left"
-             'tag "th"
-             'attribute (list "class" "column-heading-left"))
-
-          (gnc:html-document-set-style!
-             ssdoc "column-heading-center"
-             'tag "th"
-             'attribute (list "class" "column-heading-center"))
-
-          (gnc:html-document-set-style!
-             ssdoc "column-heading-right"
-             'tag "th"
-             'attribute (list "class" "column-heading-right"))
-
-          (gnc:html-document-set-style!
-             ssdoc "date-cell"
-             'tag "td"
-             'attribute (list "class" "date-cell"))
-
-          (gnc:html-document-set-style!
-             ssdoc "anchor-cell"
-             'tag "td"
-             'attribute (list "class" "anchor-cell"))
-
-          (gnc:html-document-set-style!
-             ssdoc "number-cell"
-             'tag "td"
-             'attribute (list "class" "number-cell"))
-
-          (gnc:html-document-set-style!
-             ssdoc "number-cell-neg"
-             'tag "td"
-             'attribute (list "class" "number-cell neg"))
-
-          (gnc:html-document-set-style!
-             ssdoc "number-header"
-             'tag "th"
-             'attribute (list "class" "number-header"))
-
-          (gnc:html-document-set-style!
-             ssdoc "text-cell"
-             'tag "td"
-             'attribute (list "class" "text-cell"))
-
-          (gnc:html-document-set-style!
-             ssdoc "total-number-cell"
-             'tag '("td")
-             'attribute (list "class" "total-number-cell"))
-
-          (gnc:html-document-set-style!
-             ssdoc "total-number-cell-neg"
-             'tag '("td")
-             'attribute (list "class" "total-number-cell neg"))
-
-          (gnc:html-document-set-style!
-             ssdoc "total-label-cell"
-             'tag '("td")
-             'attribute (list "class" "total-label-cell"))
-
-          (gnc:html-document-set-style!
-             ssdoc "centered-label-cell"
-             'tag '("td")
-             'attribute (list "class" "centered-label-cell"))
-        )
-        (begin ;; this is for gtkhtml
-          (gnc:html-document-set-style!
-             ssdoc "column-heading-left"
-             'tag "th"
-             'attribute (list "align" "left"))
-
-          (gnc:html-document-set-style!
-             ssdoc "column-heading-center"
-             'tag "th"
-             'attribute (list "align" "center"))
-
-          (gnc:html-document-set-style!
-             ssdoc "column-heading-right"
-             'tag "th"
-             'attribute (list "align" "right"))
-
-          (gnc:html-document-set-style!
-             ssdoc "date-cell"
-             'tag "td"
-             'attribute (list "nowrap" "nowrap"))
-
-          (gnc:html-document-set-style!
-             ssdoc "anchor-cell"
-             'tag "td"
-             'attribute (list "align" "left")
-             'attribute (list "nowrap"))
-
-          (gnc:html-document-set-style!
-             ssdoc "number-cell"
-             'tag "td"
-             'attribute (list "align" "right")
-             'attribute (list "nowrap"))
-
-          (gnc:html-document-set-style!
-             ssdoc "number-cell-neg"
-             'tag "td"
-             'attribute (list "align" "right")
-             'attribute (list "nowrap"))
-
-          (gnc:html-document-set-style!
-             ssdoc "number-header"
-             'tag "th"
-             'attribute (list "align" "right"))
-
-          (gnc:html-document-set-style!
-             ssdoc "text-cell"
-             'tag "td"
-             'attribute (list "align" "left"))
-
-          (gnc:html-document-set-style!
-             ssdoc "total-number-cell"
-             'tag '("td")
-             'attribute (list "align" "right"))
-
-          (gnc:html-document-set-style!
-             ssdoc "total-number-cell-neg"
-             'tag '("td")
-             'attribute (list "align" "right"))
-
-          (gnc:html-document-set-style!
-             ssdoc "total-label-cell"
-             'tag '("td")
-             'attribute (list "align" "left"))
-
-          (gnc:html-document-set-style!
-             ssdoc "centered-label-cell"
-             'tag '("td")
-             'attribute (list "align" "center"))
-        )
-    )
+    (gnc:html-document-set-style!
+     ssdoc "column-heading-left"
+     'tag "th"
+     'attribute (list "class" "column-heading-left"))
+
+    (gnc:html-document-set-style!
+     ssdoc "column-heading-center"
+     'tag "th"
+     'attribute (list "class" "column-heading-center"))
+
+    (gnc:html-document-set-style!
+     ssdoc "column-heading-right"
+     'tag "th"
+     'attribute (list "class" "column-heading-right"))
+
+    (gnc:html-document-set-style!
+     ssdoc "date-cell"
+     'tag "td"
+     'attribute (list "class" "date-cell"))
+
+    (gnc:html-document-set-style!
+     ssdoc "anchor-cell"
+     'tag "td"
+     'attribute (list "class" "anchor-cell"))
+
+    (gnc:html-document-set-style!
+     ssdoc "number-cell"
+     'tag "td"
+     'attribute (list "class" "number-cell"))
+
+    (gnc:html-document-set-style!
+     ssdoc "number-cell-neg"
+     'tag "td"
+     'attribute (list "class" "number-cell neg"))
+
+    (gnc:html-document-set-style!
+     ssdoc "number-header"
+     'tag "th"
+     'attribute (list "class" "number-header"))
+
+    (gnc:html-document-set-style!
+     ssdoc "text-cell"
+     'tag "td"
+     'attribute (list "class" "text-cell"))
+
+    (gnc:html-document-set-style!
+     ssdoc "total-number-cell"
+     'tag '("td")
+     'attribute (list "class" "total-number-cell"))
+
+    (gnc:html-document-set-style!
+     ssdoc "total-number-cell-neg"
+     'tag '("td")
+     'attribute (list "class" "total-number-cell neg"))
+
+    (gnc:html-document-set-style!
+     ssdoc "total-label-cell"
+     'tag '("td")
+     'attribute (list "class" "total-label-cell"))
+
+    (gnc:html-document-set-style!
+     ssdoc "centered-label-cell"
+     'tag '("td")
+     'attribute (list "class" "centered-label-cell"))
 
     (if (and bgpixmap
 	     (not (string=? bgpixmap "")))
diff --git a/gnucash/report/stylesheets/stylesheet-head-or-tail.scm b/gnucash/report/stylesheets/stylesheet-head-or-tail.scm
index d78e931..f6a279c 100644
--- a/gnucash/report/stylesheets/stylesheet-head-or-tail.scm
+++ b/gnucash/report/stylesheets/stylesheet-head-or-tail.scm
@@ -40,7 +40,7 @@
 (use-modules (gnucash core-utils)) ; for gnc:version
 (use-modules (gnucash gettext))
 
-(gnc:module-load "gnucash/html" 0)   ; added for 'gnc-html-engine-supports-css'
+(gnc:module-load "gnucash/html" 0)
 (gnc:module-load "gnucash/report/report-system" 0)
 
 (define (head-or-tail-options)
@@ -306,143 +306,70 @@
 ;;;;
 ;;;;
 ;;;;
-    (if (gnc-html-engine-supports-css)
-        (begin ;; this is for webkit
-          (gnc:html-document-set-style!
-             ssdoc "column-heading-left"
-             'tag "th"
-             'attribute (list "class" "column-heading-left"))
-
-          (gnc:html-document-set-style!
-             ssdoc "column-heading-center"
-             'tag "th"
-             'attribute (list "class" "column-heading-center"))
-
-          (gnc:html-document-set-style!
-             ssdoc "column-heading-right"
-             'tag "th"
-             'attribute (list "class" "column-heading-right"))
-
-          (gnc:html-document-set-style!
-             ssdoc "date-cell"
-             'tag "td"
-             'attribute (list "class" "date-cell"))
-
-          (gnc:html-document-set-style!
-             ssdoc "anchor-cell"
-             'tag "td"
-             'attribute (list "class" "anchor-cell"))
-
-          (gnc:html-document-set-style!
-             ssdoc "number-cell"
-             'tag "td"
-             'attribute (list "class" "number-cell"))
-
-          (gnc:html-document-set-style!
-             ssdoc "number-cell-neg"
-             'tag "td"
-             'attribute (list "class" "number-cell neg"))
-
-          (gnc:html-document-set-style!
-             ssdoc "number-header"
-             'tag "th"
-             'attribute (list "class" "number-header"))
-
-          (gnc:html-document-set-style!
-             ssdoc "text-cell"
-             'tag "td"
-             'attribute (list "class" "text-cell"))
-
-          (gnc:html-document-set-style!
-             ssdoc "total-number-cell"
-             'tag '("td")
-             'attribute (list "class" "total-number-cell"))
-
-          (gnc:html-document-set-style!
-             ssdoc "total-number-cell-neg"
-             'tag '("td")
-             'attribute (list "class" "total-number-cell neg"))
-
-          (gnc:html-document-set-style!
-             ssdoc "total-label-cell"
-             'tag '("td")
-             'attribute (list "class" "total-label-cell"))
-
-          (gnc:html-document-set-style!
-             ssdoc "centered-label-cell"
-             'tag '("td")
-             'attribute (list "class" "centered-label-cell"))
-        )
-        (begin ;; this is for gtkhtml
-          (gnc:html-document-set-style!
-             ssdoc "column-heading-left"
-             'tag "th"
-             'attribute (list "align" "left"))
-
-          (gnc:html-document-set-style!
-             ssdoc "column-heading-center"
-             'tag "th"
-             'attribute (list "align" "center"))
-
-          (gnc:html-document-set-style!
-             ssdoc "column-heading-right"
-             'tag "th"
-             'attribute (list "align" "right"))
-
-          (gnc:html-document-set-style!
-             ssdoc "date-cell"
-             'tag "td"
-             'attribute (list "nowrap" "nowrap"))
-
-          (gnc:html-document-set-style!
-             ssdoc "anchor-cell"
-             'tag "td"
-             'attribute (list "align" "left")
-             'attribute (list "nowrap"))
-
-          (gnc:html-document-set-style!
-             ssdoc "number-cell"
-             'tag "td"
-             'attribute (list "align" "right")
-             'attribute (list "nowrap"))
-
-          (gnc:html-document-set-style!
-             ssdoc "number-cell-neg"
-             'tag "td"
-             'attribute (list "align" "right")
-             'attribute (list "nowrap"))
-
-          (gnc:html-document-set-style!
-             ssdoc "number-header"
-             'tag "th"
-             'attribute (list "align" "right"))
-
-          (gnc:html-document-set-style!
-             ssdoc "text-cell"
-             'tag "td"
-             'attribute (list "align" "left"))
-
-          (gnc:html-document-set-style!
-             ssdoc "total-number-cell"
-             'tag '("td")
-             'attribute (list "align" "right"))
-
-          (gnc:html-document-set-style!
-             ssdoc "total-number-cell-neg"
-             'tag '("td")
-             'attribute (list "align" "right"))
-
-          (gnc:html-document-set-style!
-             ssdoc "total-label-cell"
-             'tag '("td")
-             'attribute (list "align" "left"))
-
-          (gnc:html-document-set-style!
-             ssdoc "centered-label-cell"
-             'tag '("td")
-             'attribute (list "align" "center"))
-        )
-    )
+    (gnc:html-document-set-style!
+     ssdoc "column-heading-left"
+     'tag "th"
+     'attribute (list "class" "column-heading-left"))
+
+    (gnc:html-document-set-style!
+     ssdoc "column-heading-center"
+     'tag "th"
+     'attribute (list "class" "column-heading-center"))
+
+    (gnc:html-document-set-style!
+     ssdoc "column-heading-right"
+     'tag "th"
+     'attribute (list "class" "column-heading-right"))
+
+    (gnc:html-document-set-style!
+     ssdoc "date-cell"
+     'tag "td"
+     'attribute (list "class" "date-cell"))
+
+    (gnc:html-document-set-style!
+     ssdoc "anchor-cell"
+     'tag "td"
+     'attribute (list "class" "anchor-cell"))
+
+    (gnc:html-document-set-style!
+     ssdoc "number-cell"
+     'tag "td"
+     'attribute (list "class" "number-cell"))
+
+    (gnc:html-document-set-style!
+     ssdoc "number-cell-neg"
+     'tag "td"
+     'attribute (list "class" "number-cell neg"))
+
+    (gnc:html-document-set-style!
+     ssdoc "number-header"
+     'tag "th"
+     'attribute (list "class" "number-header"))
+
+    (gnc:html-document-set-style!
+     ssdoc "text-cell"
+     'tag "td"
+     'attribute (list "class" "text-cell"))
+
+    (gnc:html-document-set-style!
+     ssdoc "total-number-cell"
+     'tag '("td")
+     'attribute (list "class" "total-number-cell"))
+
+    (gnc:html-document-set-style!
+     ssdoc "total-number-cell-neg"
+     'tag '("td")
+     'attribute (list "class" "total-number-cell neg"))
+
+    (gnc:html-document-set-style!
+     ssdoc "total-label-cell"
+     'tag '("td")
+     'attribute (list "class" "total-label-cell"))
+
+    (gnc:html-document-set-style!
+     ssdoc "centered-label-cell"
+     'tag '("td")
+     'attribute (list "class" "centered-label-cell"))
 
     (if (and bgpixmap
 	     (not (string=? bgpixmap "")))
diff --git a/gnucash/report/stylesheets/stylesheet-plain.scm b/gnucash/report/stylesheets/stylesheet-plain.scm
index 7a60ac4..511f00d 100644
--- a/gnucash/report/stylesheets/stylesheet-plain.scm
+++ b/gnucash/report/stylesheets/stylesheet-plain.scm
@@ -31,7 +31,7 @@
 (use-modules (srfi srfi-13))
 (use-modules (srfi srfi-14))
 
-(gnc:module-load "gnucash/html" 0)   ; added for 'gnc-html-engine-supports-css'
+(gnc:module-load "gnucash/html" 0)
 (gnc:module-load "gnucash/report/report-system" 0)
 
 ;; plain style sheet
@@ -123,143 +123,70 @@
        'attribute (list "cellspacing" spacing)
        'attribute (list "cellpadding" padding))
 
-    (if (gnc-html-engine-supports-css)
-        (begin ;; this is for webkit
-          (gnc:html-document-set-style!
-             ssdoc "column-heading-left"
-             'tag "th"
-             'attribute (list "class" "column-heading-left"))
-
-          (gnc:html-document-set-style!
-             ssdoc "column-heading-center"
-             'tag "th"
-             'attribute (list "class" "column-heading-center"))
-
-          (gnc:html-document-set-style!
-             ssdoc "column-heading-right"
-             'tag "th"
-             'attribute (list "class" "column-heading-right"))
-
-          (gnc:html-document-set-style!
-             ssdoc "date-cell"
-             'tag "td"
-             'attribute (list "class" "date-cell"))
-
-          (gnc:html-document-set-style!
-             ssdoc "anchor-cell"
-             'tag "td"
-             'attribute (list "class" "anchor-cell"))
-
-          (gnc:html-document-set-style!
-             ssdoc "number-cell"
-             'tag "td"
-             'attribute (list "class" "number-cell"))
-
-          (gnc:html-document-set-style!
-             ssdoc "number-cell-neg"
-             'tag "td"
-             'attribute (list "class" "number-cell neg"))
-
-          (gnc:html-document-set-style!
-             ssdoc "number-header"
-             'tag "th"
-             'attribute (list "class" "number-header"))
-
-          (gnc:html-document-set-style!
-             ssdoc "text-cell"
-             'tag "td"
-             'attribute (list "class" "text-cell"))
-
-          (gnc:html-document-set-style!
-             ssdoc "total-number-cell"
-             'tag "td"
-             'attribute (list "class" "total-number-cell"))
-
-          (gnc:html-document-set-style!
-             ssdoc "total-number-cell-neg"
-             'tag "td"
-             'attribute (list "class" "total-number-cell neg"))
-
-          (gnc:html-document-set-style!
-             ssdoc "total-label-cell"
-             'tag "td"
-             'attribute (list "class" "total-label-cell"))
-
-          (gnc:html-document-set-style!
-             ssdoc "centered-label-cell"
-             'tag "td"
-             'attribute (list "class" "centered-label-cell"))
-        )
-        (begin ;; this is for gtkhtml
-          (gnc:html-document-set-style!
-             ssdoc "column-heading-left"
-             'tag "th"
-             'attribute (list "align" "left"))
-
-          (gnc:html-document-set-style!
-             ssdoc "column-heading-center"
-             'tag "th"
-             'attribute (list "align" "center"))
-
-          (gnc:html-document-set-style!
-             ssdoc "column-heading-right"
-             'tag "th"
-             'attribute (list "align" "right"))
-
-          (gnc:html-document-set-style!
-             ssdoc "date-cell"
-             'tag "td"
-             'attribute (list "nowrap" "nowrap"))
-
-          (gnc:html-document-set-style!
-             ssdoc "anchor-cell"
-             'tag "td"
-             'attribute (list "align" "left")
-             'attribute (list "nowrap"))
-
-          (gnc:html-document-set-style!
-             ssdoc "number-cell"
-             'tag "td"
-             'attribute (list "align" "right")
-             'attribute (list "nowrap"))
-
-          (gnc:html-document-set-style!
-             ssdoc "number-cell-neg"
-             'tag "td"
-             'attribute (list "align" "right")
-             'attribute (list "nowrap"))
-
-          (gnc:html-document-set-style!
-             ssdoc "number-header"
-             'tag "th"
-             'attribute (list "align" "right"))
-
-          (gnc:html-document-set-style!
-             ssdoc "text-cell"
-             'tag "td"
-             'attribute (list "align" "left"))
-
-          (gnc:html-document-set-style!
-             ssdoc "total-number-cell"
-             'tag "td"
-             'attribute (list "align" "right"))
-
-          (gnc:html-document-set-style!
-             ssdoc "total-number-cell-neg"
-             'tag "td"
-             'attribute (list "align" "right"))
-
-          (gnc:html-document-set-style!
-             ssdoc "total-label-cell"
-             'tag "td"
-             'attribute (list "align" "left"))
-
-          (gnc:html-document-set-style!
-             ssdoc "centered-label-cell"
-             'tag "td"
-             'attribute (list "align" "center"))
-        )
-    )
+    (gnc:html-document-set-style!
+     ssdoc "column-heading-left"
+     'tag "th"
+     'attribute (list "class" "column-heading-left"))
+
+    (gnc:html-document-set-style!
+     ssdoc "column-heading-center"
+     'tag "th"
+     'attribute (list "class" "column-heading-center"))
+
+    (gnc:html-document-set-style!
+     ssdoc "column-heading-right"
+     'tag "th"
+     'attribute (list "class" "column-heading-right"))
+
+    (gnc:html-document-set-style!
+     ssdoc "date-cell"
+     'tag "td"
+     'attribute (list "class" "date-cell"))
+
+    (gnc:html-document-set-style!
+     ssdoc "anchor-cell"
+     'tag "td"
+     'attribute (list "class" "anchor-cell"))
+
+    (gnc:html-document-set-style!
+     ssdoc "number-cell"
+     'tag "td"
+     'attribute (list "class" "number-cell"))
+
+    (gnc:html-document-set-style!
+     ssdoc "number-cell-neg"
+     'tag "td"
+     'attribute (list "class" "number-cell neg"))
+
+    (gnc:html-document-set-style!
+     ssdoc "number-header"
+     'tag "th"
+     'attribute (list "class" "number-header"))
+
+    (gnc:html-document-set-style!
+     ssdoc "text-cell"
+     'tag "td"
+     'attribute (list "class" "text-cell"))
+
+    (gnc:html-document-set-style!
+     ssdoc "total-number-cell"
+     'tag "td"
+     'attribute (list "class" "total-number-cell"))
+
+    (gnc:html-document-set-style!
+     ssdoc "total-number-cell-neg"
+     'tag "td"
+     'attribute (list "class" "total-number-cell neg"))
+
+    (gnc:html-document-set-style!
+     ssdoc "total-label-cell"
+     'tag "td"
+     'attribute (list "class" "total-label-cell"))
+
+    (gnc:html-document-set-style!
+     ssdoc "centered-label-cell"
+     'tag "td"
+     'attribute (list "class" "centered-label-cell"))
 
     (gnc:html-document-set-style!
        ssdoc "normal-row"

commit 0dc9a115017b4608e87ffb9a36734ac01079cb8a
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sat Jun 2 16:46:05 2018 +0800

    [html-table] compact html-table-append-row!

diff --git a/gnucash/report/report-system/html-table.scm b/gnucash/report/report-system/html-table.scm
index 66c273e..41a615f 100644
--- a/gnucash/report/report-system/html-table.scm
+++ b/gnucash/report/report-system/html-table.scm
@@ -339,16 +339,12 @@
     
 
 (define (gnc:html-table-append-row! table newrow)
-  (let* ((dd (gnc:html-table-data table))
-	 (current-num-rows (gnc:html-table-num-rows table))
-	 (new-num-rows (+ current-num-rows 1)))
-    (if (list? newrow)
-        (set! dd (cons newrow dd))
-        (set! dd (cons (list newrow) dd)))
-    (gnc:html-table-set-num-rows-internal! 
-     table 
-     new-num-rows)
-    (gnc:html-table-set-data! table dd)
+  (let* ((current-num-rows (gnc:html-table-num-rows table))
+	 (new-num-rows (1+ current-num-rows)))
+    (gnc:html-table-set-num-rows-internal! table new-num-rows)
+    (gnc:html-table-set-data! table
+                              (cons (if (list? newrow) newrow (list newrow))
+                                    (gnc:html-table-data table)))
     new-num-rows))
 
 (define (gnc:html-table-remove-last-row! table)

commit 5532de0481888701432d592d396fbcc8a3138855
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sat Jun 2 16:43:31 2018 +0800

    [html-table] use srfi-1

diff --git a/gnucash/report/report-system/html-table.scm b/gnucash/report/report-system/html-table.scm
index 2f224e3..66c273e 100644
--- a/gnucash/report/report-system/html-table.scm
+++ b/gnucash/report/report-system/html-table.scm
@@ -326,14 +326,7 @@
   (record-modifier <html-table> 'num-rows))
 
 (define (gnc:html-table-num-columns table)
-  (let ((max 0))
-    (for-each 
-     (lambda (row)
-       (let ((l (length row))) 
-         (if (> l max)
-             (set! max l))))
-     (gnc:html-table-data table))
-    max))
+  (apply max (map length (gnc:html-table-data table))))
 
 (define (gnc:html-table-append-row/markup! table markup newrow)
   (let ((rownum (gnc:html-table-append-row! table newrow)))

commit 7927597b3a7e043a9993f5af5d33222f09886f82
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sat Jun 2 16:25:58 2018 +0800

    [html-table] gnc:html-table-set-row-style! uses cons*
    
    This cons* call creates a pair-terminated list.

diff --git a/gnucash/report/report-system/html-table.scm b/gnucash/report/report-system/html-table.scm
index 157dcdc..2f224e3 100644
--- a/gnucash/report/report-system/html-table.scm
+++ b/gnucash/report/report-system/html-table.scm
@@ -746,8 +746,5 @@
     retval))
 
 (define (gnc:html-table-set-last-row-style! table tag . rest)
-  (let ((arg-list
-         (cons table
-               (cons (1- (gnc:html-table-num-rows table))
-                     (cons tag rest)))))
-    (apply gnc:html-table-set-row-style! arg-list)))
+  (apply gnc:html-table-set-row-style!
+         (cons* table (1- (gnc:html-table-num-rows table)) tag rest)))

commit 39e976045b72e6d3cac178dc9db970b08b13bdb7
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sat Jun 2 16:25:41 2018 +0800

    [html-table] unused gnc:html-table-merge
    
    This function is not used through code.

diff --git a/gnucash/report/report-system/html-table.scm b/gnucash/report/report-system/html-table.scm
index 405e4ad..157dcdc 100644
--- a/gnucash/report/report-system/html-table.scm
+++ b/gnucash/report/report-system/html-table.scm
@@ -613,18 +613,6 @@
 ;; Feel free to contribute! :-)
 ;; 
 
-;; This function was moved here from balance-sheet.scm.
-;; This function "stacks" the two tables vertically.
-(define (gnc:html-table-merge t1 t2)
-  (begin 
-    (gnc:html-table-set-data! t1
-			      (append
-			       (gnc:html-table-data t2)
-			       (gnc:html-table-data t1)))
-    (gnc:html-table-set-num-rows-internal!
-     t1 (+ (gnc:html-table-num-rows t1)
-           (gnc:html-table-num-rows t2)))))
-
 (define (gnc:html-table-render table doc)
   (let* ((retval '())
          (push (lambda (l) (set! retval (cons l retval)))))
diff --git a/gnucash/report/report-system/report-system.scm b/gnucash/report/report-system/report-system.scm
index 3ed2b71..70943f2 100644
--- a/gnucash/report/report-system/report-system.scm
+++ b/gnucash/report/report-system/report-system.scm
@@ -620,7 +620,6 @@
 (export gnc:html-table-set-cell/tag!)
 (export gnc:html-table-append-column!)
 (export gnc:html-table-prepend-column!)
-(export gnc:html-table-merge)
 (export gnc:html-table-render)
 
 ;; html-text.scm

commit 03b57e515386039b3231c9a12f5674232c8ab972
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Fri Jun 1 17:44:09 2018 +0800

    [hello-world] amend comment from timepair to time64

diff --git a/gnucash/report/utility-reports/hello-world.scm b/gnucash/report/utility-reports/hello-world.scm
index 8cc6b1d..d149888 100644
--- a/gnucash/report/utility-reports/hello-world.scm
+++ b/gnucash/report/utility-reports/hello-world.scm
@@ -93,8 +93,8 @@
       "c" (N_ "This is a string option.") (N_ "Hello, World")))
     
     ;; This is a date/time option. The user can pick a date and,
-    ;; possibly, a time. Times are stored as a pair
-    ;; (seconds . nanoseconds) measured from Jan 1, 1970, i.e.,
+    ;; possibly, a time. Times are stored as an integer specifying
+    ;; number of seconds measured from Jan 1, 1970, i.e.,
     ;; Unix time. The last option is false, so the user can only
     ;; select a date, not a time. The default value is the current
     ;; time.

commit 879ec75f20ea72cf8860e4a732c3df900ecace19
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Thu May 24 11:16:23 2018 +0800

    [TR] [ENH] grid can easily display averages
    
    The subtotal-grid can easily calculate average amounts per *row*. I
    think this is useful enough to be set as default. Enable if number of
    secondary-key groups > 1. Upgrade test suite as well.
    
    The subtotal collectors do not calculate overall secondary-key
    subtotals, therefore, we cannot easily calculate average amounts per
    *column*.

diff --git a/gnucash/report/standard-reports/test/test-transaction.scm b/gnucash/report/standard-reports/test/test-transaction.scm
index 675792c..2abd884 100644
--- a/gnucash/report/standard-reports/test/test-transaction.scm
+++ b/gnucash/report/standard-reports/test/test-transaction.scm
@@ -805,46 +805,46 @@
       (let ((sxml (options->sxml options "subtotal table")))
         (test-equal "summary bank-row is correct"
           (list "Bank" "$190.00" "$190.00" "$190.00" "$190.00" "$190.00" "$190.00"
-                "$190.00" "$190.00" "$190.00" "$190.00" "$190.00" "$190.00" "$2,280.00")
+                "$190.00" "$190.00" "$190.00" "$190.00" "$190.00" "$190.00" "$2,280.00" "$190.00")
           (get-row-col sxml 1 #f))
         (test-equal "summary gbp bank-row is correct"
           (list "GBP Bank" "£51.00" "£51.00" "£51.00" "£51.00" "£51.00" "£51.00"
-                "£51.00" "£51.00" "£51.00" "£51.00" "£51.00" "£51.00" "£612.00")
+                "£51.00" "£51.00" "£51.00" "£51.00" "£51.00" "£51.00" "£612.00" "£51.00")
           (get-row-col sxml 2 #f))
         (test-equal "summary expense-row is correct"
           (list "Expenses" "$22.00" "$22.00" "$22.00" "$22.00" "$22.00" "$22.00"
-                "$22.00" "$22.00" "$22.00" "$22.00" "$22.00" "$22.00" "$264.00")
+                "$22.00" "$22.00" "$22.00" "$22.00" "$22.00" "$22.00" "$264.00" "$22.00")
           (get-row-col sxml 3 #f))
         (test-equal "summary income-row is correct"
           (list "Income" "-$212.00" "-$212.00" "-$212.00" "-$212.00" "-$212.00"
                 "-$212.00" "-$212.00" "-$212.00" "-$212.00" "-$212.00" "-$212.00"
-                "-$212.00" "-$2,544.00")
+                "-$212.00" "-$2,544.00" "-$212.00")
           (get-row-col sxml 4 #f))
         (test-equal "summary gbp income-row is correct"
           (list "Income-GBP" "-£51.00" "-£51.00" "-£51.00" "-£51.00" "-£51.00" "-£51.00"
-                "-£51.00" "-£51.00" "-£51.00" "-£51.00" "-£51.00" "-£51.00" "-£612.00")
+                "-£51.00" "-£51.00" "-£51.00" "-£51.00" "-£51.00" "-£51.00" "-£612.00" "-£51.00")
           (get-row-col sxml 5 #f))
         (test-equal "summary gbp total-row is correct"
-          (list "Grand Total" "£0.00")
+          (list "Grand Total" "£0.00" "£0.00")
           (get-row-col sxml 6 #f))
         (test-equal "summary total-row is correct"
-          (list "$0.00")
+          (list "$0.00" "$0.00")
           (get-row-col sxml 7 #f)))
 
       (set-option! options "General" "Start Date" (cons 'absolute (gnc-dmy2time64 01 01 1969)))
       (set-option! options "General" "End Date" (cons 'absolute (gnc-dmy2time64 31 12 1970)))
       (let ((sxml (options->sxml options "sparse subtotal table")))
         (test-equal "sparse summary-table - row 1"
-          (list "Bank" "$29.00" "-$5.00" "-$23.00" "$1.00")
+          (list "Bank" "$29.00" "-$5.00" "-$23.00" "$1.00" "$0.33")
           (get-row-col sxml 1 #f))
         (test-equal "sparse summary-table - row 2"
-          (list "Expenses" "$16.00" "$15.00" "$31.00")
+          (list "Expenses" "$16.00" "$15.00" "$31.00" "$10.33")
           (get-row-col sxml 2 #f))
         (test-equal "sparse summary-table - row 3"
-          (list "Income" "-$29.00" "-$29.00")
+          (list "Income" "-$29.00" "-$29.00" "-$9.67")
           (get-row-col sxml 3 #f))
         (test-equal "sparse summary-table - row 4"
-          (list "Grand Total" "$3.00")
+          (list "Grand Total" "$3.00" "$1.00")
           (get-row-col sxml 4 #f))
         (test-equal "sparse summary-table - col 1"
           (list "Bank" "Expenses" "Income" "Grand Total")
@@ -860,6 +860,9 @@
           (get-row-col sxml #f 4))
         (test-equal "sparse summary-table - col 5"
           (list "$1.00" "$31.00" "-$29.00" "$3.00")
-          (get-row-col sxml #f 5))))
+          (get-row-col sxml #f 5))
+        (test-equal "sparse summary-table - col 6 average"
+          (list "$0.33" "$10.33" "-$9.67" "$1.00")
+          (get-row-col sxml #f 6))))
     (test-end "subtotal table")
     ))
diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 3599be5..2be2d86 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -1712,6 +1712,14 @@ be excluded from periodic reporting.")
   (set! grid (cons (vector row col data) grid)) ;add again. this is fine because the grid should
   grid)                                         ;never have duplicate data in the trep.
 (define (grid->html-table grid list-of-rows list-of-cols)
+  (define row-average-enabled? (> (length list-of-cols) 1))
+  (define (monetary-div monetary divisor)
+    (and monetary
+         (let* ((amount (gnc:gnc-monetary-amount monetary))
+                (currency (gnc:gnc-monetary-commodity monetary))
+                (scu (gnc-commodity-get-fraction currency)))
+           (gnc:make-gnc-monetary
+            currency (gnc-numeric-convert (/ amount divisor) scu GNC-HOW-RND-ROUND)))))
   (define (row->num-of-commodities row)
     ;; for a row, find the maximum number of commodities being stored
     (apply max
@@ -1720,22 +1728,27 @@ be excluded from periodic reporting.")
                     (if (null? cell) 0
                         (length (vector-ref (car cell) 2)))))
                 (cons 'col-total list-of-cols))))
-  (define (make-table-cell row col commodity-idx)
+  (define (make-table-cell row col commodity-idx divisor)
     (let ((cell (grid-get grid row col)))
       (if (null? cell) ""
-          (gnc:make-html-table-cell/markup "number-cell" (list-ref-safe (vector-ref (car cell) 2) commodity-idx)))))
+          (gnc:make-html-table-cell/markup "number-cell"
+                                           (monetary-div (list-ref-safe (vector-ref (car cell) 2) commodity-idx) divisor)))))
   (define (make-row row commodity-idx)
     (append
      (list (cond
             ((positive? commodity-idx) "")
             ((eq? row 'row-total) (_ "Grand Total"))
             (else (cdr row))))
-     (map (lambda (col) (make-table-cell row col commodity-idx))
+     (map (lambda (col) (make-table-cell row col commodity-idx 1))
           list-of-cols)
-     (list (make-table-cell row 'col-total commodity-idx))))
+     (list (make-table-cell row 'col-total commodity-idx 1))
+     (if row-average-enabled?
+         (list (make-table-cell row 'col-total commodity-idx (length list-of-cols)))
+         '())))
   (let ((table (gnc:make-html-table)))
     (gnc:html-table-set-caption! table optname-grid)
-    (gnc:html-table-set-col-headers! table (append (list "") (map cdr list-of-cols) (list (_ "Total"))))
+    (gnc:html-table-set-col-headers! table (append (list "") (map cdr list-of-cols) (list (_ "Total"))
+                                                   (if row-average-enabled? (list (_ "Average")) '())))
     (gnc:html-table-set-style! table "th"
                                'attribute (list "class" "column-heading-right"))
     (for-each

commit e9535bfab6da71a31aa99962e42878e5b6634f64
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun May 20 20:54:58 2018 +0800

    [TR] [bugfix] disable grid if primary-key doesn't have subtotal
    
    Formerly the logic would allow grid if secondary-subtotal enabled but
    primary-subtotal disabled. This would produce a single-row grid. This
    is now known to be inaccurate.
    
    This is because there will be multiple secondary subtotals spread out
    in the main table but no primary subtotal grouping to separate
    them. This would make every subsequent secondary subtotal overwrite
    the previous one. I think it is best to disable grid altogether if
    primary-subtotal is not enabled.
    
    The converse i.e. primary-subtotal enabled but secondary-subtotal
    disabled, is handled correctly. It produces a single-column grid.

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index bda30e8..3599be5 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -1982,6 +1982,9 @@ be excluded from periodic reporting.")
                             (qof-print-date enddate)))))
 
                 (if (and (opt-val gnc:pagename-display optname-grid)
+                         (if (memq primary-key DATE-SORTING-TYPES)
+                             (keylist-get-info date-subtotal-list primary-date-subtotal 'renderer-fn)
+                             (opt-val pagename-sorting optname-prime-subtotal))
                          (eq? (opt-val gnc:pagename-display (N_ "Amount")) 'single))
                     (let* ((generic<? (lambda (a b)
                                         (cond ((string? (car a)) (string<? (car a) (car b)))

commit aafd46a44281aecf0d140f97a15d0b7a4d546658
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Mon May 21 21:58:25 2018 +0800

    [TR] [bugfix] upgrade add-subtotal-row & grid for >1 commodities.
    
    This aims to handle multiple commodities correctly. If a row contains
    subtotals with more than one commodity, the row is duplicated so that
    every commodity gets its own line in the grid.
    
    This is accompanied by an upgrade to the test suite.
    
    Sample output if prime-sortkey = accounts, sec-sortkey = monthly dates
    
            Jan-17  Feb-17  Mar-17   Total
    Food    $22.00  $23.00  $35.00  $80.00
    Books    $8.50   $9.55  $15.00  $33.05
    Apps     £2.55   £5.00   £9.60  £17.15
    Total                          $113.05
                                    £17.15
    
    (note monthly totals are not displayed because they're not actually
    generated with the above sorting options)

diff --git a/gnucash/report/standard-reports/test/test-transaction.scm b/gnucash/report/standard-reports/test/test-transaction.scm
index df83c84..675792c 100644
--- a/gnucash/report/standard-reports/test/test-transaction.scm
+++ b/gnucash/report/standard-reports/test/test-transaction.scm
@@ -115,6 +115,7 @@
               (list "USD Bank")
               (list "Wallet"))
         (list "Income" (list (cons 'type ACCT-TYPE-INCOME)))
+        (list "Income-GBP" (list (cons 'type ACCT-TYPE-INCOME)))
         (list "Expenses" (list (cons 'type ACCT-TYPE-EXPENSE)))
         (list "Liabilities" (list (cons 'type ACCT-TYPE-LIABILITY)))
         (list "Equity" (list (cons 'type ACCT-TYPE-EQUITY)))
@@ -134,6 +135,7 @@
          (usd-bank (cdr (assoc "USD Bank" account-alist)))
          (wallet (cdr (assoc "Wallet" account-alist)))
          (income (cdr (assoc "Income" account-alist)))
+         (gbp-income (cdr (assoc "Income-GBP" account-alist)))
          (expense (cdr (assoc "Expenses" account-alist)))
          (liability (cdr (assoc "Liabilities" account-alist)))
          (equity (cdr (assoc "Equity" account-alist)))
@@ -168,7 +170,10 @@
        (xaccAccountSetCommodity (cdr pair) (gnc-default-report-currency)))
      account-alist)
 
-    ;; Here we set foreign banks' currencies
+    ;; Here we set foreign currencies
+
+    (gnc-commodity-set-user-symbol foreign2 "£")
+
     (with-account
      gbp-bank
      (lambda ()
@@ -176,6 +181,12 @@
        (xaccAccountSetCommodity gbp-bank foreign2)))
 
     (with-account
+     gbp-income
+     (lambda ()
+       (xaccAccountSetCode gbp-income "01-GBP")
+       (xaccAccountSetCommodity gbp-income foreign2)))
+
+    (with-account
      usd-bank
      (lambda ()
        (xaccAccountSetCode usd-bank "02-USD")
@@ -239,7 +250,9 @@
     ;; run in modern times, otherwise these transactions will be mixed
     ;; up with the old transactions above. The year end net bank balance
     ;; should be (* 12 (+ 103 109 -22)) = $2280.
+    ;; there will also be a £51 income monthly, tested at end of file
     (for-each (lambda (m)
+                (env-transfer env 08 (1+ m) YEAR gbp-income gbp-bank 51 #:description "£51 income")
                 (env-transfer env 03 (1+ m) YEAR income bank  103 #:description "$103 income")
                 (env-transfer env 15 (1+ m) YEAR bank expense  22 #:description "$22 expense")
                 (env-transfer env 09 (1+ m) YEAR income bank  109 #:description "$109 income"))
@@ -728,13 +741,13 @@
       (set-option! options "Sorting" "Secondary Subtotal for Date Key" 'quarterly)
       (set-option! options "Sorting" "Show Informal Debit/Credit Headers" #t)
       (set-option! options "Sorting" "Show Account Description" #t)
-      (let* ((sxml (options->sxml options "sorting=date")))
+      (let* ((sxml (options->sxml options "sorting=date, friendly headers")))
         (test-equal "expense acc friendly headers"
           '("\n" "Expenses" "\n" "Expense" "\n" "Rebate")
-          (get-row-col sxml 47 #f))
+          (get-row-col sxml 69 #f))
         (test-equal "income acc friendly headers"
           '("\n" "Income" "\n" "Charge" "\n" "Income")
-          (get-row-col sxml 69 #f)))
+          (get-row-col sxml 91 #f)))
 
       (set-option! options "Accounts" "Accounts" (list bank))
       (set-option! options "Display" "Totals" #f)
@@ -781,7 +794,7 @@
     (test-begin "subtotal table")
 
     (let ((options (default-testing-options)))
-      (set-option! options "Accounts" "Accounts" (list bank income expense))
+      (set-option! options "Accounts" "Accounts" (list bank gbp-bank gbp-income income expense))
       (set-option! options "General" "Start Date" (cons 'relative 'start-cal-year))
       (set-option! options "General" "End Date" (cons 'relative 'end-cal-year))
       (set-option! options "Display" "Subtotal Table" #t)
@@ -794,18 +807,29 @@
           (list "Bank" "$190.00" "$190.00" "$190.00" "$190.00" "$190.00" "$190.00"
                 "$190.00" "$190.00" "$190.00" "$190.00" "$190.00" "$190.00" "$2,280.00")
           (get-row-col sxml 1 #f))
+        (test-equal "summary gbp bank-row is correct"
+          (list "GBP Bank" "£51.00" "£51.00" "£51.00" "£51.00" "£51.00" "£51.00"
+                "£51.00" "£51.00" "£51.00" "£51.00" "£51.00" "£51.00" "£612.00")
+          (get-row-col sxml 2 #f))
         (test-equal "summary expense-row is correct"
           (list "Expenses" "$22.00" "$22.00" "$22.00" "$22.00" "$22.00" "$22.00"
                 "$22.00" "$22.00" "$22.00" "$22.00" "$22.00" "$22.00" "$264.00")
-          (get-row-col sxml 2 #f))
+          (get-row-col sxml 3 #f))
         (test-equal "summary income-row is correct"
           (list "Income" "-$212.00" "-$212.00" "-$212.00" "-$212.00" "-$212.00"
                 "-$212.00" "-$212.00" "-$212.00" "-$212.00" "-$212.00" "-$212.00"
                 "-$212.00" "-$2,544.00")
-          (get-row-col sxml 3 #f))
+          (get-row-col sxml 4 #f))
+        (test-equal "summary gbp income-row is correct"
+          (list "Income-GBP" "-£51.00" "-£51.00" "-£51.00" "-£51.00" "-£51.00" "-£51.00"
+                "-£51.00" "-£51.00" "-£51.00" "-£51.00" "-£51.00" "-£51.00" "-£612.00")
+          (get-row-col sxml 5 #f))
+        (test-equal "summary gbp total-row is correct"
+          (list "Grand Total" "£0.00")
+          (get-row-col sxml 6 #f))
         (test-equal "summary total-row is correct"
-          (list "Grand Total" "$0.00")
-          (get-row-col sxml 4 #f)))
+          (list "$0.00")
+          (get-row-col sxml 7 #f)))
 
       (set-option! options "General" "Start Date" (cons 'absolute (gnc-dmy2time64 01 01 1969)))
       (set-option! options "General" "End Date" (cons 'absolute (gnc-dmy2time64 31 12 1970)))
diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index b191b9f..bda30e8 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -1375,9 +1375,11 @@ be excluded from periodic reporting.")
                                   (cons (gnc:make-html-table-cell/markup "total-number-cell" mon)
                                         result)))))))))
 
-        ;; we only wish to add the first column into the grid.
-        (if (pair? columns)
-            (set! grid (grid-add grid row col (car columns))))
+        ;; take the first column of each commodity, add onto the subtotal grid
+        (set! grid (grid-add grid row col
+                             (map (lambda (commodity)
+                                    (retrieve-commodity (car columns) commodity))
+                                  list-of-commodities)))
 
         ;; each commodity subtotal gets a separate line in the html-table
         ;; each line comprises: indenting, first-column, data-columns
@@ -1689,17 +1691,17 @@ be excluded from periodic reporting.")
 ;; grid data structure
 (define (make-grid)
   '())
+(define (cell-match? cell row col)
+  (and (or (not row) (equal? row (vector-ref cell 0)))
+       (or (not col) (equal? col (vector-ref cell 1)))))
 (define (grid-get grid row col)    ; grid filter - get all row/col - if #f then retrieve whole row/col
   (filter
-   (lambda (cell)
-     (and (or (not row) (equal? row (vector-ref cell 0)))
-          (or (not col) (equal? col (vector-ref cell 1)))))
+   (lambda (cell) (cell-match? cell row col))
    grid))
 (define (grid-del grid row col)    ; grid filter - del all row/col - if #f then delete whole row/col - CAREFUL!
   (filter
    (lambda (cell)
-     (not (and (or (not row) (equal? row (vector-ref cell 0)))
-               (or (not col) (equal? col (vector-ref cell 1))))))
+     (not (cell-match? cell row col)))
    grid))
 (define (grid-rows grid)
   (delete-duplicates (map (lambda (cell) (vector-ref cell 0)) grid)))
@@ -1710,17 +1712,27 @@ be excluded from periodic reporting.")
   (set! grid (cons (vector row col data) grid)) ;add again. this is fine because the grid should
   grid)                                         ;never have duplicate data in the trep.
 (define (grid->html-table grid list-of-rows list-of-cols)
-  (define (make-table-cell row col)
+  (define (row->num-of-commodities row)
+    ;; for a row, find the maximum number of commodities being stored
+    (apply max
+           (map (lambda (col)
+                  (let ((cell (grid-get grid row col)))
+                    (if (null? cell) 0
+                        (length (vector-ref (car cell) 2)))))
+                (cons 'col-total list-of-cols))))
+  (define (make-table-cell row col commodity-idx)
     (let ((cell (grid-get grid row col)))
-      (if (pair? cell)
-          (gnc:make-html-table-cell/markup "number-cell" (car (vector-ref (car cell) 2)))
-          "")))
-  (define (make-row row)
+      (if (null? cell) ""
+          (gnc:make-html-table-cell/markup "number-cell" (list-ref-safe (vector-ref (car cell) 2) commodity-idx)))))
+  (define (make-row row commodity-idx)
     (append
-     (list (if (eq? row 'row-total) (_ "Grand Total") (cdr row)))
-     (map (lambda (col) (make-table-cell row col))
+     (list (cond
+            ((positive? commodity-idx) "")
+            ((eq? row 'row-total) (_ "Grand Total"))
+            (else (cdr row))))
+     (map (lambda (col) (make-table-cell row col commodity-idx))
           list-of-cols)
-     (list (make-table-cell row 'col-total))))
+     (list (make-table-cell row 'col-total commodity-idx))))
   (let ((table (gnc:make-html-table)))
     (gnc:html-table-set-caption! table optname-grid)
     (gnc:html-table-set-col-headers! table (append (list "") (map cdr list-of-cols) (list (_ "Total"))))
@@ -1728,10 +1740,12 @@ be excluded from periodic reporting.")
                                'attribute (list "class" "column-heading-right"))
     (for-each
      (lambda (row)
-       (gnc:html-table-append-row! table (make-row row)))
-     list-of-rows)
-    (if (memq 'row-total (grid-rows grid))
-        (gnc:html-table-append-row! table (make-row 'row-total)))
+       (for-each (lambda (commodity-idx)
+                   (gnc:html-table-append-row! table (make-row row commodity-idx)))
+                 (iota (row->num-of-commodities row))))
+     (if (memq 'row-total (grid-rows grid))
+         (append list-of-rows '(row-total))
+         list-of-rows))
     table))
 
 ;; ;;;;;;;;;;;;;;;;;;;;

commit 98964f7a6d50107331a5eed70ddada7250d5108c
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Mon May 21 21:56:31 2018 +0800

    [TR] refactor add-subtotal-row
    
    Schemify to use (append) rather than (set!) calls to build
    list-of-cells. This is (IMHO) neater and definitely favoured by
    seasoned schemers.

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 966246c..b191b9f 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -1309,8 +1309,7 @@ be excluded from periodic reporting.")
             (gnc:html-table-append-row/markup! table subheading-style (reverse row-contents)))))
 
     (define (add-subtotal-row subtotal-string subtotal-collectors subtotal-style level row col)
-      (let* ((row-contents '())
-             (left-indent (case level
+      (let* ((left-indent (case level
                             ((total) 0)
                             ((primary) primary-indent)
                             ((secondary) (+ primary-indent secondary-indent))))
@@ -1321,26 +1320,26 @@ be excluded from periodic reporting.")
                                                      gnc-commodity-equal)))
 
         (define (retrieve-commodity list-of-monetary commodity)
-          (if (null? list-of-monetary)
-              #f
-              (if (gnc-commodity-equal (gnc:gnc-monetary-commodity (car list-of-monetary)) commodity)
-                  (car list-of-monetary)
-                  (retrieve-commodity (cdr list-of-monetary) commodity))))
+          (find (lambda (mon) (gnc-commodity-equal commodity (gnc:gnc-monetary-commodity mon)))
+                list-of-monetary))
 
-        (define (add-first-column string)
+        (define (first-column string)
           (if export?
-              (begin
-                (addto! row-contents (gnc:make-html-table-cell/markup "total-label-cell" string))
-                (for-each (lambda (cell) (addto! row-contents cell))
-                          (gnc:html-make-empty-cells (+ right-indent width-left-columns -1))))
-              (addto! row-contents (gnc:make-html-table-cell/size/markup 1 (+ right-indent width-left-columns) "total-label-cell" string))))
+              (cons
+               (gnc:make-html-table-cell/markup "total-label-cell" string)
+               (gnc:html-make-empty-cells (+ right-indent width-left-columns -1)))
+              (list
+               (gnc:make-html-table-cell/size/markup 1 (+ right-indent width-left-columns) "total-label-cell" string))))
 
-        (define (add-columns commodity)
+        (define (data-columns commodity)
           (let loop ((merging? #f)
                      (last-column #f)
                      (columns columns)
-                     (merge-list merge-list))
-            (if (not (null? columns))
+                     (merge-list merge-list)
+                     (result '()))
+            (if (null? columns)
+                ;; we've processed all columns. return the (reversed) list of html-table-cells.
+                (reverse result)
                 (let* ((mon (retrieve-commodity (car columns) commodity))
                        (this-column (and mon (gnc:gnc-monetary-amount mon))))
                   (if (car merge-list)
@@ -1348,7 +1347,8 @@ be excluded from periodic reporting.")
                       (loop #t
                             this-column
                             (cdr columns)
-                            (cdr merge-list))
+                            (cdr merge-list)
+                            result)
                       (begin
                         (if merging?
                             ;; We're completing merge. Display debit-credit in correct column.
@@ -1360,38 +1360,37 @@ be excluded from periodic reporting.")
                                                               commodity (abs sum)))))
                                    (debit-col (and sum (positive? sum) sum-table-cell))
                                    (credit-col (and sum (not (positive? sum)) sum-table-cell)))
-                              (addto! row-contents (or debit-col ""))
-                              (addto! row-contents (or credit-col "")))
-                            ;; Default; not merging nor completed merge. Display monetary amount
-                            (addto! row-contents (gnc:make-html-table-cell/markup "total-number-cell" mon)))
-                        (loop #f
-                              #f
-                              (cdr columns)
-                              (cdr merge-list))))))))
+                              (loop #f
+                                    #f
+                                    (cdr columns)
+                                    (cdr merge-list)
+                                    (cons* (or credit-col "")
+                                           (or debit-col "")
+                                           result)))
+                            ;; Default; not merging nor completed merge. Just add amount to result.
+                            (loop #f
+                                  #f
+                                  (cdr columns)
+                                  (cdr merge-list)
+                                  (cons (gnc:make-html-table-cell/markup "total-number-cell" mon)
+                                        result)))))))))
 
         ;; we only wish to add the first column into the grid.
         (if (pair? columns)
             (set! grid (grid-add grid row col (car columns))))
 
-        ;;first row
-        (for-each (lambda (cell) (addto! row-contents cell))
-                  (gnc:html-make-empty-cells left-indent))
-        (add-first-column subtotal-string)
-        (add-columns (if (pair? list-of-commodities)
-                         (car list-of-commodities)
-                         #f)) ;to account for empty-row subtotals
-        (gnc:html-table-append-row/markup! table subtotal-style (reverse row-contents))
-
-        ;;subsequent rows
-        (if (pair? list-of-commodities)
-            (for-each (lambda (commodity)
-                        (set! row-contents '())
-                        (for-each (lambda (cell) (addto! row-contents cell))
-                                  (gnc:html-make-empty-cells left-indent))
-                        (add-first-column "")
-                        (add-columns commodity)
-                        (gnc:html-table-append-row/markup! table subtotal-style (reverse row-contents)))
-                      (cdr list-of-commodities)))))
+        ;; each commodity subtotal gets a separate line in the html-table
+        ;; each line comprises: indenting, first-column, data-columns
+        (let loop ((first-column-string subtotal-string)
+                   (list-of-commodities list-of-commodities))
+          (unless (null? list-of-commodities)
+            (gnc:html-table-append-row/markup!
+             table subtotal-style
+             (append
+              (gnc:html-make-empty-cells left-indent)
+              (first-column first-column-string)
+              (data-columns (car list-of-commodities))))
+            (loop "" (cdr list-of-commodities))))))
 
     (define (total-string str) (string-append (_ "Total For ") str))
 



Summary of changes:
 CMakeLists.txt                                     |   51 +-
 NEWS                                               |  152 +-
 borrowed/CMakeLists.txt                            |    4 +-
 cmake/README_CMAKE.txt                             |    5 +-
 common/cmake_modules/GncAddTest.cmake              |   10 +-
 common/cmake_modules/MakeDist.cmake                |    2 +-
 gnucash/CMakeLists.txt                             |    3 +-
 gnucash/gnome-utils/CMakeLists.txt                 |    3 +-
 gnucash/gnome-utils/dialog-utils.c                 |   62 +-
 gnucash/gnome-utils/dialog-utils.h                 |    2 +
 gnucash/gnome-utils/gnc-file.c                     |   10 +-
 gnucash/gnome-utils/gnc-main-window.c              |   48 +-
 gnucash/gnome/gnc-budget-view.c                    |   42 +-
 gnucash/gnucash.rc.in                              |    8 +-
 gnucash/gtkbuilder/assistant-stock-split.glade     |    4 +-
 gnucash/gtkbuilder/dialog-account-picker.glade     |    2 +-
 gnucash/import-export/CMakeLists.txt               |    1 -
 .../csv-imp/assistant-csv-trans-import.cpp         |   19 +-
 .../import-export/csv-imp/gnc-imp-settings-csv.hpp |    1 +
 gnucash/import-export/csv-imp/gnc-tokenizer.cpp    |   20 +-
 .../import-export/qif-imp/assistant-qif-import.c   |  123 +-
 gnucash/import-export/qif-imp/qif-file.scm         |    2 +-
 gnucash/import-export/qif-imp/qif-to-gnc.scm       |   41 +-
 gnucash/import-export/qif/CMakeLists.txt           |   43 -
 gnucash/import-export/qif/qif-context.c            |  417 ------
 gnucash/import-export/qif/qif-defaults.c           |  152 --
 gnucash/import-export/qif/qif-defaults.h           |   44 -
 gnucash/import-export/qif/qif-file.c               |  326 -----
 gnucash/import-export/qif/qif-file.h               |   36 -
 gnucash/import-export/qif/qif-import-p.h           |  100 --
 gnucash/import-export/qif/qif-import.h             |  159 ---
 gnucash/import-export/qif/qif-objects-p.h          |  174 ---
 gnucash/import-export/qif/qif-objects.c            | 1468 --------------------
 gnucash/import-export/qif/qif-objects.h            |   71 -
 gnucash/import-export/qif/qif-parse.c              |  935 -------------
 gnucash/import-export/qif/qif-parse.h              |   50 -
 gnucash/import-export/qif/test/CMakeLists.txt      |   18 -
 .../qif/test/test-files/test-1-bank-txn.qif        |    6 -
 gnucash/import-export/qif/test/test-link.c         |   28 -
 gnucash/import-export/qif/test/test-qif.c          |  110 --
 .../register/ledger-core/split-register-control.c  |    7 +
 .../register/ledger-core/split-register-model.c    |   95 +-
 gnucash/register/register-core/table-allgui.c      |   19 +
 gnucash/register/register-core/table-allgui.h      |    2 +
 gnucash/register/register-core/table-model.c       |   37 +
 gnucash/register/register-core/table-model.h       |   17 +-
 gnucash/register/register-gnome/datecell-gnome.c   |   93 +-
 gnucash/register/register-gnome/gnucash-sheet.c    |   67 +-
 gnucash/register/register-gnome/table-gnome.c      |    7 +-
 gnucash/report/business-reports/CMakeLists.txt     |    5 +-
 .../report/business-reports/balsheet-eg.eguile.scm |    9 -
 gnucash/report/business-reports/balsheet-eg.scm    |   13 +-
 gnucash/report/business-reports/easy-invoice.scm   |    2 +-
 gnucash/report/business-reports/receipt.eguile.scm |    2 -
 gnucash/report/business-reports/receipt.scm        |   15 +-
 .../report/business-reports/taxinvoice.eguile.scm  |    2 -
 gnucash/report/business-reports/taxinvoice.scm     |   15 +-
 .../report/business-reports/test/CMakeLists.txt    |    1 +
 .../report/business-reports/test/test-invoice.scm  |  590 ++++++++
 gnucash/report/locale-specific/us/taxtxf.scm       |   87 +-
 gnucash/report/report-system/CMakeLists.txt        |    1 +
 .../report/report-system/eguile-html-utilities.scm |    4 +-
 gnucash/report/report-system/html-acct-table.scm   |    5 +-
 gnucash/report/report-system/html-anytag.scm       |  121 ++
 gnucash/report/report-system/html-document.scm     |    9 +-
 gnucash/report/report-system/html-fonts.scm        |    3 +-
 gnucash/report/report-system/html-table.scm        |   44 +-
 gnucash/report/report-system/html-text.scm         |    2 +-
 gnucash/report/report-system/report-system.scm     |   16 +-
 gnucash/report/report-system/report.scm            |   51 +-
 gnucash/report/report-system/test/CMakeLists.txt   |    1 +
 gnucash/report/report-system/test/test-extras.scm  |   48 +-
 .../report-system/test/test-report-system.scm      |   79 ++
 gnucash/report/standard-reports/CMakeLists.txt     |    3 +-
 gnucash/report/standard-reports/balance-sheet.scm  |    3 +-
 gnucash/report/standard-reports/net-barchart.scm   |  492 -------
 gnucash/report/standard-reports/net-charts.scm     |  601 ++++++++
 gnucash/report/standard-reports/net-linechart.scm  |  550 --------
 .../report/standard-reports/test/CMakeLists.txt    |   18 +-
 .../standard-reports/test/test-balance-sheet.scm   |  140 ++
 .../test/test-cashflow-barchart.scm                |    6 +-
 .../report/standard-reports/test/test-charts.scm   |  134 ++
 .../test/test-generic-category-report.scm          |  292 ----
 .../test/test-generic-net-barchart.scm             |  366 -----
 .../test/test-generic-net-linechart.scm            |  225 ---
 .../standard-reports/test/test-income-gst.scm      |    8 +-
 .../test/test-standard-category-report.scm         |  267 +++-
 .../test/test-standard-net-barchart.scm            |  344 ++++-
 .../test/test-standard-net-linechart.scm           |  212 ++-
 .../standard-reports/test/test-transaction.scm     |  142 +-
 gnucash/report/standard-reports/transaction.scm    |  308 ++--
 gnucash/report/stylesheets/stylesheet-easy.scm     |  205 +--
 gnucash/report/stylesheets/stylesheet-fancy.scm    |  205 +--
 gnucash/report/stylesheets/stylesheet-footer.scm   |  205 +--
 .../report/stylesheets/stylesheet-head-or-tail.scm |  205 +--
 gnucash/report/stylesheets/stylesheet-plain.scm    |  205 +--
 gnucash/report/utility-reports/hello-world.scm     |    4 +-
 libgnucash/app-utils/gnc-sx-instance-model.c       |  131 +-
 libgnucash/app-utils/test/CMakeLists.txt           |    1 +
 .../backend/dbi/test/test-backend-dbi-basic.cpp    |    7 +-
 .../backend/sql/gnc-sql-column-table-entry.cpp     |    4 +-
 .../backend/sql/gnc-sql-column-table-entry.hpp     |   18 +-
 libgnucash/backend/sql/gnc-sql-object-backend.hpp  |    1 +
 libgnucash/backend/xml/gnc-entry-xml-v2.cpp        |    2 +-
 libgnucash/backend/xml/gnc-invoice-xml-v2.cpp      |    2 +-
 libgnucash/backend/xml/gnc-order-xml-v2.cpp        |    2 +-
 libgnucash/backend/xml/gnc-pricedb-xml-v2.cpp      |    2 +-
 libgnucash/backend/xml/gnc-transaction-xml-v2.cpp  |    6 +-
 libgnucash/backend/xml/io-gncxml-v1.cpp            |    2 +-
 libgnucash/engine/gnc-date.cpp                     |    3 +
 libgnucash/engine/gnc-engine.c                     |    4 +-
 libgnucash/engine/qofbook.cpp                      |  131 +-
 libgnucash/engine/qofbook.h                        |    7 +
 libgnucash/engine/test/test-extras.scm             |   20 +
 libgnucash/scm/CMakeLists.txt                      |    2 +-
 po/POTFILES.in                                     |    7 +-
 116 files changed, 4221 insertions(+), 7492 deletions(-)
 delete mode 100644 gnucash/import-export/qif/CMakeLists.txt
 delete mode 100644 gnucash/import-export/qif/qif-context.c
 delete mode 100644 gnucash/import-export/qif/qif-defaults.c
 delete mode 100644 gnucash/import-export/qif/qif-defaults.h
 delete mode 100644 gnucash/import-export/qif/qif-file.c
 delete mode 100644 gnucash/import-export/qif/qif-file.h
 delete mode 100644 gnucash/import-export/qif/qif-import-p.h
 delete mode 100644 gnucash/import-export/qif/qif-import.h
 delete mode 100644 gnucash/import-export/qif/qif-objects-p.h
 delete mode 100644 gnucash/import-export/qif/qif-objects.c
 delete mode 100644 gnucash/import-export/qif/qif-objects.h
 delete mode 100644 gnucash/import-export/qif/qif-parse.c
 delete mode 100644 gnucash/import-export/qif/qif-parse.h
 delete mode 100644 gnucash/import-export/qif/test/CMakeLists.txt
 delete mode 100644 gnucash/import-export/qif/test/test-files/test-1-bank-txn.qif
 delete mode 100644 gnucash/import-export/qif/test/test-link.c
 delete mode 100644 gnucash/import-export/qif/test/test-qif.c
 create mode 100644 gnucash/report/business-reports/test/test-invoice.scm
 create mode 100644 gnucash/report/report-system/html-anytag.scm
 create mode 100644 gnucash/report/report-system/test/test-report-system.scm
 delete mode 100644 gnucash/report/standard-reports/net-barchart.scm
 create mode 100644 gnucash/report/standard-reports/net-charts.scm
 delete mode 100644 gnucash/report/standard-reports/net-linechart.scm
 create mode 100644 gnucash/report/standard-reports/test/test-balance-sheet.scm
 create mode 100644 gnucash/report/standard-reports/test/test-charts.scm
 delete mode 100644 gnucash/report/standard-reports/test/test-generic-category-report.scm
 delete mode 100644 gnucash/report/standard-reports/test/test-generic-net-barchart.scm
 delete mode 100644 gnucash/report/standard-reports/test/test-generic-net-linechart.scm



More information about the gnucash-changes mailing list