From 6add6b1f1d2b1da7a4a2f5e2d4c4067d56d14632 Mon Sep 17 00:00:00 2001 From: "Dr. Peter Zimmerer" Date: Sat, 22 Feb 2020 18:08:54 +0100 Subject: [PATCH 1/3] New callback functions for matching online accounts with GnuCash accounts Introducing a dedicated callback function to test two AqBanking accounts for equality and an associated hash function returning the account unique id as hash value. --- .../import-export/aqb/assistant-ab-initial.c | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/gnucash/import-export/aqb/assistant-ab-initial.c b/gnucash/import-export/aqb/assistant-ab-initial.c index 133e3a70de..5aaea509fa 100644 --- a/gnucash/import-export/aqb/assistant-ab-initial.c +++ b/gnucash/import-export/aqb/assistant-ab-initial.c @@ -27,6 +27,7 @@ * @author Copyright (C) 2006 David Hampton * @author Copyright (C) 2008 Andreas Koehler * @author Copyright (C) 2011 Robert Fewell + * @author Copyright (C) 2020 Peter Zimmerer */ #include @@ -89,6 +90,10 @@ gboolean aai_key_press_event_cb(GtkWidget *widget, GdkEventKey *event, gpointer void aai_wizard_page_prepare (GtkAssistant *assistant, gpointer user_data); void aai_wizard_button_clicked_cb(GtkButton *button, gpointer user_data); +#ifdef AQBANKING6 +static guint aai_ab_account_hash(gconstpointer v); +static gboolean aai_ab_account_equal(gconstpointer v1, gconstpointer v2); +#endif void aai_match_page_prepare (GtkAssistant *assistant, gpointer user_data); static gboolean banking_has_accounts(AB_BANKING *banking); @@ -290,6 +295,32 @@ aai_wizard_button_clicked_cb(GtkButton *button, gpointer user_data) LEAVE(" "); } +#ifdef AQBANKING6 +static guint +aai_ab_account_hash (gconstpointer v) +{ + if (v == NULL) + return 0; + else + /* Use the account unique id as hash value */ + return AB_AccountSpec_GetUniqueId((const GNC_AB_ACCOUNT_SPEC *) v); +} + +static gboolean +aai_ab_account_equal (gconstpointer v1, gconstpointer v2) +{ + if (v1 == NULL || v2 == NULL) + return v1 == v2; + else + { + /* Use the account unique id to check for equality */ + uint32_t uid1 = AB_AccountSpec_GetUniqueId((const GNC_AB_ACCOUNT_SPEC *) v1); + uint32_t uid2 = AB_AccountSpec_GetUniqueId((const GNC_AB_ACCOUNT_SPEC *) v2); + return uid1 == uid2; + } +} +#endif + void aai_match_page_prepare (GtkAssistant *assistant, gpointer user_data) { @@ -311,7 +342,11 @@ aai_match_page_prepare (GtkAssistant *assistant, gpointer user_data) #endif /* Determine current mapping */ root = gnc_book_get_root_account(gnc_get_current_book()); +#ifdef AQBANKING6 + info->gnc_hash = g_hash_table_new(&aai_ab_account_hash, &aai_ab_account_equal); +#else info->gnc_hash = g_hash_table_new(&g_direct_hash, &g_direct_equal); +#endif data.api = info->api; data.hash = info->gnc_hash; gnc_account_foreach_descendant( From 768b25dfd61ae25f697ddacf445cb30a7ca17222 Mon Sep 17 00:00:00 2001 From: "Dr. Peter Zimmerer" Date: Sat, 22 Feb 2020 18:21:26 +0100 Subject: [PATCH 2/3] Using equality check from commit 2f38095 for removal of unassigned GC accounts Using the equality check introduced with the previous commit 2f38095 also for checking if a newly assigned GnuCash account has been previously assigned to a different AqBanking account and deleting the previous assignment to keep the matches unique. --- gnucash/import-export/aqb/assistant-ab-initial.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gnucash/import-export/aqb/assistant-ab-initial.c b/gnucash/import-export/aqb/assistant-ab-initial.c index 5aaea509fa..6ecb72f294 100644 --- a/gnucash/import-export/aqb/assistant-ab-initial.c +++ b/gnucash/import-export/aqb/assistant-ab-initial.c @@ -545,7 +545,11 @@ clear_line_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gtk_tree_model_get(model, iter, ACCOUNT_LIST_COL_AB_ACCT, &ab_acc, -1); +#ifdef AQBANKING6 + if (aai_ab_account_equal(ab_acc, data->ab_acc)) +#else if (ab_acc == data->ab_acc) +#endif { gtk_list_store_set(store, iter, ACCOUNT_LIST_COL_GNC_NAME, "", ACCOUNT_LIST_COL_CHECKED, TRUE, -1); From d4f024eff77c22bce6a7ba8a130bd4745eb57a6d Mon Sep 17 00:00:00 2001 From: "Dr. Peter Zimmerer" Date: Sat, 22 Feb 2020 19:13:55 +0100 Subject: [PATCH 3/3] Clear hbci KVP from accounts no longer associated with an online one A reverse hash table is introduced in order to memorize the matched GnuCash accounts before starting the matching wizard. This hash table is used within the aai_on_finish callback to delete only the KVPs of those GnuCash accounts which are no longer matched with an AqBanking account. All other GnuCash accounts (previously matched and currently matched) are just updated with the new assignments. --- .../import-export/aqb/assistant-ab-initial.c | 55 ++++++++++++++----- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/gnucash/import-export/aqb/assistant-ab-initial.c b/gnucash/import-export/aqb/assistant-ab-initial.c index 6ecb72f294..13425a67ca 100644 --- a/gnucash/import-export/aqb/assistant-ab-initial.c +++ b/gnucash/import-export/aqb/assistant-ab-initial.c @@ -106,7 +106,9 @@ static gboolean find_gnc_acc_cb(gpointer key, gpointer value, gpointer user_data static gboolean clear_line_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer user_data); static void account_list_clicked_cb (GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *col, gpointer user_data); -static void clear_kvp_acc_cb(Account *gnc_acc, gpointer user_data); +static void insert_acc_into_revhash_cb(gpointer ab_acc, gpointer gnc_acc, gpointer revhash); +static void remove_acc_from_revhash_cb(gpointer ab_acc, gpointer gnc_acc, gpointer revhash); +static void clear_kvp_acc_cb(gpointer key, gpointer value, gpointer user_data); static void save_kvp_acc_cb(gpointer key, gpointer value, gpointer user_data); static void aai_close_handler(gpointer user_data); @@ -127,6 +129,8 @@ struct _ABInitialInfo AB_BANKING *api; /* AB_ACCOUNT* -> Account* -- DO NOT DELETE THE KEYS! */ GHashTable *gnc_hash; + /* Reverse hash table for lookup of matched GnuCash accounts */ + GHashTable *gnc_revhash; }; struct _DeferredInfo @@ -206,6 +210,12 @@ aai_destroy_cb(GtkWidget *object, gpointer user_data) info->gnc_hash = NULL; } + if (info->gnc_revhash) + { + g_hash_table_destroy(info->gnc_revhash); + info->gnc_revhash = NULL; + } + if (info->api) { gnc_AB_BANKING_delete(info->api); @@ -321,6 +331,20 @@ aai_ab_account_equal (gconstpointer v1, gconstpointer v2) } #endif +static void +insert_acc_into_revhash_cb(gpointer ab_acc, gpointer gnc_acc, gpointer revhash) +{ + g_return_if_fail(revhash && gnc_acc && ab_acc); + g_hash_table_insert((GHashTable *) revhash, gnc_acc, ab_acc); +} + +static void +remove_acc_from_revhash_cb(gpointer ab_acc, gpointer gnc_acc, gpointer revhash) +{ + g_return_if_fail(revhash && gnc_acc); + g_hash_table_remove((GHashTable *) revhash, gnc_acc); +} + void aai_match_page_prepare (GtkAssistant *assistant, gpointer user_data) { @@ -349,8 +373,10 @@ aai_match_page_prepare (GtkAssistant *assistant, gpointer user_data) #endif data.api = info->api; data.hash = info->gnc_hash; - gnc_account_foreach_descendant( - root, (AccountCb) hash_from_kvp_acc_cb, &data); + gnc_account_foreach_descendant(root, (AccountCb) hash_from_kvp_acc_cb, &data); + /* Memorize initial matches in reverse hash table */ + info->gnc_revhash = g_hash_table_new(NULL, NULL); + g_hash_table_foreach(data.hash, (GHFunc) insert_acc_into_revhash_cb, (gpointer) info->gnc_revhash); info->match_page_prepared = TRUE; } @@ -365,13 +391,17 @@ void aai_on_finish (GtkAssistant *assistant, gpointer user_data) { ABInitialInfo *info = user_data; - Account *root; - g_return_if_fail(info && info->gnc_hash); + g_return_if_fail(info && info->gnc_hash && info->gnc_revhash); + /* Remove GnuCash accounts from reverse hash table which are still + * matched to an AqBanking account. For the remaining GnuCash accounts + * the KVPs must be cleared (i.e. deleted). + * Please note that the value (i.e. the GnuCash account) stored in info->gnc_hash + * is used as key for info->gnc_revhash */ + g_hash_table_foreach(info->gnc_hash, (GHFunc) remove_acc_from_revhash_cb, info->gnc_revhash); /* Commit the changes */ - root = gnc_book_get_root_account(gnc_get_current_book()); - gnc_account_foreach_descendant(root, (AccountCb) clear_kvp_acc_cb, NULL); + g_hash_table_foreach(info->gnc_revhash, (GHFunc) clear_kvp_acc_cb, NULL); g_hash_table_foreach(info->gnc_hash, (GHFunc) save_kvp_acc_cb, NULL); gtk_widget_destroy(info->window); @@ -649,14 +679,11 @@ account_list_clicked_cb (GtkTreeView *view, GtkTreePath *path, } static void -clear_kvp_acc_cb(Account *gnc_acc, gpointer user_data) +clear_kvp_acc_cb(gpointer gnc_acc, gpointer ab_acc, gpointer user_data) { - if (gnc_ab_get_account_uid(gnc_acc)) - gnc_ab_set_account_uid(gnc_acc, 0); - if (gnc_ab_get_account_accountid(gnc_acc)) - gnc_ab_set_account_accountid(gnc_acc, ""); - if (gnc_ab_get_account_bankcode(gnc_acc)) - gnc_ab_set_account_bankcode(gnc_acc, ""); + g_return_if_fail(gnc_acc); + /* Delete complete "hbci..." KVPs for GnuCash account */ + gnc_account_delete_map_entry((Account *) gnc_acc, "hbci", NULL, NULL, FALSE); } static void