From 9bc722f601de2a907f31b507163d80716aa9aebb Mon Sep 17 00:00:00 2001 From: jean Date: Sat, 18 Apr 2020 22:31:03 -0700 Subject: [PATCH 1/3] Add reconcile immediately after ofx import, based on statement To make this work, I had to add the right head and make a change to the CMakeLists file. The ofx code used to have a static int to count imported transactions, which was a bit hacky. I replaced it with a structure that's passed to all the callbacks. The structure has the transaction count, as well as a list of statement balances information. This list is used after the import of the ofx file to do the reconciliation. NOTE: I wouldn't need a list since the current code only process one ofx at a time. However I have another PR for importing several ofx files in one shot, so this PR is ready for that. The reconciliation code is modeled after what's done in aqbanking. --- gnucash/import-export/ofx/CMakeLists.txt | 2 +- gnucash/import-export/ofx/gnc-ofx-import.c | 81 +++++++++++++------ .../ledger-core/gnc-ledger-display2.c | 1 - 3 files changed, 59 insertions(+), 25 deletions(-) diff --git a/gnucash/import-export/ofx/CMakeLists.txt b/gnucash/import-export/ofx/CMakeLists.txt index 07f8b9ab04..5b216332e5 100644 --- a/gnucash/import-export/ofx/CMakeLists.txt +++ b/gnucash/import-export/ofx/CMakeLists.txt @@ -21,7 +21,7 @@ if (WITH_OFX) add_library(gncmod-ofx ${ofx_SOURCES} ${ofx_noinst_HEADERS}) target_link_libraries(gncmod-ofx gnc-generic-import gnc-engine gnc-app-utils gnc-core-utils - gncmod-gnome-utils ${LIBOFX_LDFLAGS}) + gncmod-gnome-utils gnc-gnome ${LIBOFX_LDFLAGS}) target_compile_definitions(gncmod-ofx PRIVATE -DG_LOG_DOMAIN=\"gnc.import.ofx\") diff --git a/gnucash/import-export/ofx/gnc-ofx-import.c b/gnucash/import-export/ofx/gnc-ofx-import.c index e0da8b4175..8d476cf366 100644 --- a/gnucash/import-export/ofx/gnc-ofx-import.c +++ b/gnucash/import-export/ofx/gnc-ofx-import.c @@ -50,6 +50,7 @@ #include "gnc-ui.h" #include "dialog-account.h" #include "dialog-utils.h" +#include "window-reconcile.h" #define GNC_PREFS_GROUP "dialogs.import.ofx" #define GNC_PREF_AUTO_COMMODITY "auto-create-commodity" @@ -66,7 +67,13 @@ static QofLogModule log_module = GNC_MOD_IMPORT; GNCImportMainMatcher *gnc_ofx_importer_gui = NULL; static gboolean auto_create_commodity = FALSE; static Account *ofx_parent_account = NULL; -static gint num_trans_processed = 0; +// Structure we use to gather information about statement balance/account etc. +typedef struct _ofx_info +{ + gint num_trans_processed; + GSList* statement; + GtkWindow* parent; +} ofx_info ; GList *ofx_created_commodites = NULL; @@ -111,6 +118,7 @@ set_associated_income_account(Account* investment_account, xaccAccountCommitEdit(investment_account); } +int ofx_proc_statement_cb(struct OfxStatementData data, void * statement_user_data); int ofx_proc_security_cb(const struct OfxSecurityData data, void * security_user_data); int ofx_proc_transaction_cb (struct OfxTransactionData data, void *user_data); int ofx_proc_account_cb(struct OfxAccountData data, void * account_user_data); @@ -409,7 +417,8 @@ int ofx_proc_transaction_cb(struct OfxTransactionData data, void *user_data) Transaction *transaction; Split *split; gchar *notes, *tmp; - GtkWindow *parent = GTK_WINDOW (user_data); + ofx_info* info = (ofx_info*) user_data; + GtkWindow *parent = GTK_WINDOW (info->parent); g_assert(gnc_ofx_importer_gui); @@ -890,16 +899,20 @@ int ofx_proc_transaction_cb(struct OfxTransactionData data, void *user_data) xaccTransDestroy(transaction); xaccTransCommitEdit(transaction); } - num_trans_processed += 1; + info->num_trans_processed += 1; return 0; }//end ofx_proc_transaction() -/* -int ofx_proc_statement_cb(struct OfxStatementData data, void * statement_user_data) + +int ofx_proc_statement_cb (struct OfxStatementData data, void * statement_user_data) { - return 0; + ofx_info* info = (ofx_info*) statement_user_data; + struct OfxStatementData* statement = g_new (struct OfxStatementData, 1); + *statement = data; + info->statement = g_slist_append (info->statement, statement); + return 0; }//end ofx_proc_statement() -*/ + int ofx_proc_account_cb(struct OfxAccountData data, void * account_user_data) { @@ -910,6 +923,8 @@ int ofx_proc_account_cb(struct OfxAccountData data, void * account_user_data) /* In order to trigger a book options display on the creation of a new book, * we need to detect when we are dealing with a new book. */ gboolean new_book = gnc_is_new_book(); + ofx_info* info = (ofx_info*) account_user_data; + Account* account = NULL; const gchar * account_type_name = _("Unknown OFX account"); @@ -985,10 +1000,10 @@ int ofx_proc_account_cb(struct OfxAccountData data, void * account_user_data) "%s \"%s\"", account_type_name, data.account_name); - gnc_import_select_account(gnc_gen_trans_list_widget(gnc_ofx_importer_gui), - data.account_id, 1, - account_description, default_commodity, - default_type, NULL, NULL); + account = gnc_import_select_account( gnc_gen_trans_list_widget(gnc_ofx_importer_gui), + data.account_id, 1, + account_description, default_commodity, + default_type, NULL, NULL); g_free(account_description); } else @@ -1034,6 +1049,9 @@ void gnc_file_ofx_import (GtkWindow *parent) LibofxContextPtr libofx_context = libofx_get_new_context(); GList *filters = NULL; GtkFileFilter* filter = gtk_file_filter_new (); + GSList *iter = NULL; + // Create the structure we're using to gather reconciliation information. + ofx_info info = {0, NULL, parent}; ofx_PARSER_msg = false; ofx_DEBUG_msg = false; @@ -1077,12 +1095,11 @@ void gnc_file_ofx_import (GtkWindow *parent) auto_create_commodity = gnc_prefs_get_bool (GNC_PREFS_GROUP_IMPORT, GNC_PREF_AUTO_COMMODITY); - /* Initialize libofx */ - - /*ofx_set_statement_cb(libofx_context, ofx_proc_statement_cb, 0);*/ - ofx_set_account_cb(libofx_context, ofx_proc_account_cb, 0); - ofx_set_transaction_cb(libofx_context, ofx_proc_transaction_cb, parent); - ofx_set_security_cb(libofx_context, ofx_proc_security_cb, 0); + /* Initialize libofx and set the callbacks*/ + ofx_set_statement_cb (libofx_context, ofx_proc_statement_cb, &info); + ofx_set_account_cb (libofx_context, ofx_proc_account_cb, &info); + ofx_set_transaction_cb (libofx_context, ofx_proc_transaction_cb, &info); + ofx_set_security_cb (libofx_context, ofx_proc_security_cb, &info); /*ofx_set_status_cb(libofx_context, ofx_proc_status_cb, 0);*/ #ifdef G_OS_WIN32 @@ -1092,21 +1109,39 @@ void gnc_file_ofx_import (GtkWindow *parent) #endif DEBUG("Opening selected file"); - num_trans_processed = 0; libofx_proc_file(libofx_context, selected_filename, AUTODETECT); - // Now would be a good time to see whether the view has anything in it! + // See whether the view has anything in it and warn the user if not. if(gnc_gen_trans_list_empty(gnc_ofx_importer_gui)) { gnc_gen_trans_list_delete (gnc_ofx_importer_gui); - if(num_trans_processed) - gnc_info_dialog(parent,_("OFX file imported, %d transactions processed, no transactions to match"),num_trans_processed); + if(info.num_trans_processed) + gnc_info_dialog (parent, _("OFX file imported, %d transactions processed, no transactions to match"), info.num_trans_processed); } else { gnc_gen_trans_list_show_all(gnc_ofx_importer_gui); } - g_free(selected_filename); - + // Open a reconcile window for each balance statement found. + for (iter=info.statement; iter; iter=iter->next) + { + struct OfxStatementData* statement = (struct OfxStatementData*) iter->data; + Account* account = gnc_import_select_account (gnc_gen_trans_list_widget(gnc_ofx_importer_gui), + statement->account_id, + 0, NULL, NULL, ACCT_TYPE_NONE, + NULL, NULL); + if (account) + { + // Grab the balance value and date from the statement and open a reconcile window for this account. + gnc_numeric value = double_to_gnc_numeric (-statement->ledger_balance, xaccAccountGetCommoditySCU (account), GNC_HOW_RND_ROUND_HALF_UP); + recnWindowWithBalance (GTK_WIDGET (parent), + account, + value, + statement->ledger_balance_date); + } + g_free (statement); + } + g_free (selected_filename); + g_slist_free (info.statement); } if (ofx_created_commodites) diff --git a/gnucash/register/ledger-core/gnc-ledger-display2.c b/gnucash/register/ledger-core/gnc-ledger-display2.c index 6df238c9ba..cfedaec60a 100644 --- a/gnucash/register/ledger-core/gnc-ledger-display2.c +++ b/gnucash/register/ledger-core/gnc-ledger-display2.c @@ -809,7 +809,6 @@ gnc_ledger_display2_internal (Account *lead_account, Query *q, ld->use_double_line_default = use_double_line; - // JEAN: add mismatched_commodities ld->model = gnc_tree_model_split_reg_new (reg_type, style, use_double_line, is_template, mismatched_commodities); gnc_tree_model_split_reg_set_data (ld->model, ld, gnc_ledger_display2_parent); From 07cbd936c6019f938b4075f80201b4d51226054c Mon Sep 17 00:00:00 2001 From: jean Date: Sun, 19 Apr 2020 14:00:41 -0700 Subject: [PATCH 2/3] Add detection of account type to handle balance sign --- gnucash/import-export/ofx/gnc-ofx-import.c | 27 ++++++++++++++++------ 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/gnucash/import-export/ofx/gnc-ofx-import.c b/gnucash/import-export/ofx/gnc-ofx-import.c index 8d476cf366..179e3c6866 100644 --- a/gnucash/import-export/ofx/gnc-ofx-import.c +++ b/gnucash/import-export/ofx/gnc-ofx-import.c @@ -1131,14 +1131,27 @@ void gnc_file_ofx_import (GtkWindow *parent) NULL, NULL); if (account) { - // Grab the balance value and date from the statement and open a reconcile window for this account. - gnc_numeric value = double_to_gnc_numeric (-statement->ledger_balance, xaccAccountGetCommoditySCU (account), GNC_HOW_RND_ROUND_HALF_UP); - recnWindowWithBalance (GTK_WIDGET (parent), - account, - value, - statement->ledger_balance_date); + if (statement->ledger_balance_valid) + { + // The balance needs to be corrected for credit-card and loan accounts. + float sign = 1; + switch(xaccAccountGetType (account)) + { + case ACCT_TYPE_CREDIT: + case ACCT_TYPE_LIABILITY: + sign = -1; + default: + sign = 1; + } + // Grab the balance value and date from the statement and open a reconcile window for this account. + gnc_numeric value = double_to_gnc_numeric (sign*statement->ledger_balance, xaccAccountGetCommoditySCU (account), GNC_HOW_RND_ROUND_HALF_UP); + recnWindowWithBalance (GTK_WIDGET (parent), + account, + value, + statement->ledger_balance_date); + } + g_free (statement); } - g_free (statement); } g_free (selected_filename); g_slist_free (info.statement); From 2d0d7c40f88f89c084066ceccc110ece62c1f0a9 Mon Sep 17 00:00:00 2001 From: jean Date: Tue, 21 Apr 2020 22:24:09 -0700 Subject: [PATCH 3/3] Make the reconcile window close when the session closes, use full version of free --- gnucash/gnome/window-reconcile.c | 3 +++ gnucash/import-export/ofx/gnc-ofx-import.c | 16 ++---------- libgnucash/engine/Account.cpp | 29 ++++++++++++++++++++++ libgnucash/engine/Account.h | 5 ++++ 4 files changed, 39 insertions(+), 14 deletions(-) diff --git a/gnucash/gnome/window-reconcile.c b/gnucash/gnome/window-reconcile.c index 0930849f25..689cb257a6 100644 --- a/gnucash/gnome/window-reconcile.c +++ b/gnucash/gnome/window-reconcile.c @@ -57,6 +57,7 @@ #include "gnc-window.h" #include "reconcile-view.h" #include "window-reconcile.h" +#include "gnc-session.h" #ifdef MAC_INTEGRATION #include #endif @@ -1765,6 +1766,8 @@ recnWindowWithBalance (GtkWidget *parent, Account *account, gnc_numeric new_endi gnc_register_gui_component (WINDOW_RECONCILE_CM_CLASS, refresh_handler, close_handler, recnData); + // This window should close if we close the session. + gnc_gui_component_set_session (recnData->component_id, gnc_get_current_session()); recn_set_watches (recnData); diff --git a/gnucash/import-export/ofx/gnc-ofx-import.c b/gnucash/import-export/ofx/gnc-ofx-import.c index 179e3c6866..05edb4d0f3 100644 --- a/gnucash/import-export/ofx/gnc-ofx-import.c +++ b/gnucash/import-export/ofx/gnc-ofx-import.c @@ -1133,28 +1133,16 @@ void gnc_file_ofx_import (GtkWindow *parent) { if (statement->ledger_balance_valid) { - // The balance needs to be corrected for credit-card and loan accounts. - float sign = 1; - switch(xaccAccountGetType (account)) - { - case ACCT_TYPE_CREDIT: - case ACCT_TYPE_LIABILITY: - sign = -1; - default: - sign = 1; - } - // Grab the balance value and date from the statement and open a reconcile window for this account. - gnc_numeric value = double_to_gnc_numeric (sign*statement->ledger_balance, xaccAccountGetCommoditySCU (account), GNC_HOW_RND_ROUND_HALF_UP); + gnc_numeric value = double_to_gnc_numeric (statement->ledger_balance, xaccAccountGetCommoditySCU (account), GNC_HOW_RND_ROUND_HALF_UP); recnWindowWithBalance (GTK_WIDGET (parent), account, value, statement->ledger_balance_date); } - g_free (statement); } } g_free (selected_filename); - g_slist_free (info.statement); + g_slist_free_full (info.statement,g_free); } if (ofx_created_commodites) diff --git a/libgnucash/engine/Account.cpp b/libgnucash/engine/Account.cpp index b35e4555cb..33dd04a5e5 100644 --- a/libgnucash/engine/Account.cpp +++ b/libgnucash/engine/Account.cpp @@ -4391,6 +4391,35 @@ gboolean xaccAccountIsAssetLiabType(GNCAccountType t) } } +GNCAccountType +xaccAccountTypeGetFundamental (GNCAccountType t) +{ + switch (t) + { + case ACCT_TYPE_BANK: + case ACCT_TYPE_STOCK: + case ACCT_TYPE_MUTUAL: + case ACCT_TYPE_CURRENCY: + case ACCT_TYPE_CASH: + case ACCT_TYPE_ASSET: + case ACCT_TYPE_RECEIVABLE: + return ACCT_TYPE_ASSET; + case ACCT_TYPE_CREDIT: + case ACCT_TYPE_LIABILITY: + case ACCT_TYPE_PAYABLE: + return ACCT_TYPE_LIABILITY; + case ACCT_TYPE_INCOME: + return ACCT_TYPE_INCOME; + case ACCT_TYPE_EXPENSE: + return ACCT_TYPE_EXPENSE; + case ACCT_TYPE_EQUITY: + return ACCT_TYPE_EQUITY; + case ACCT_TYPE_TRADING: + default: + return ACCT_TYPE_NONE; + } +} + gboolean xaccAccountIsAPARType(GNCAccountType t) { switch (t) diff --git a/libgnucash/engine/Account.h b/libgnucash/engine/Account.h index 4ba18066e2..04b7b35c72 100644 --- a/libgnucash/engine/Account.h +++ b/libgnucash/engine/Account.h @@ -975,6 +975,11 @@ guint32 xaccAccountTypesValid(void); * Asset or Liability type, but not a business account type * (meaning not an Accounts Payable/Accounts Receivable). */ gboolean xaccAccountIsAssetLiabType(GNCAccountType t); + +/** Convenience function to return the fundamental type + * asset/liability/income/expense/equity given an account type. */ +GNCAccountType xaccAccountTypeGetFundamental (GNCAccountType t); + /** Convenience function to check if the account is a valid * business account type