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.
This commit is contained in:
jean
2020-04-18 22:31:03 -07:00
parent 0cfb40efeb
commit 9bc722f601
3 changed files with 59 additions and 25 deletions

View File

@@ -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\")

View File

@@ -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)

View File

@@ -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);