From 62af7f0d6d479dffcaebfcecd4a8af13aa49fc88 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Mon, 16 Feb 2026 16:50:58 +0000 Subject: [PATCH] Bug 799734 - mapped OFX income account not listed Change the Import Map Editor to look for the KVP slot "ofx/associated-income-account" so it can be listed. The account reference stored is a GUID so add a function to retrieve that so the existing gnc_account_delete_map_entry function can be used to remove it. --- gnucash/gnome/dialog-imap-editor.c | 150 +++++++++++++++-------- libgnucash/engine/Account.cpp | 14 +++ libgnucash/engine/Account.h | 5 + libgnucash/engine/test/utest-Account.cpp | 25 ++++ 4 files changed, 144 insertions(+), 50 deletions(-) diff --git a/gnucash/gnome/dialog-imap-editor.c b/gnucash/gnome/dialog-imap-editor.c index 581ef2c766..c20d5a7b4d 100644 --- a/gnucash/gnome/dialog-imap-editor.c +++ b/gnucash/gnome/dialog-imap-editor.c @@ -703,6 +703,97 @@ get_account_info_nbayes (ImapDialog *imap_dialog, GList *accts) } } +static void +add_online_entry_to_store (ImapDialog *imap_dialog, GtkTreeIter *toplevel, + const gchar *based_on, GncImapInfo *imapInfo) +{ + // Add top level entry and pass iter to add_to_store + gtk_tree_store_append (GTK_TREE_STORE(imap_dialog->model), toplevel, NULL); + add_to_store (imap_dialog, toplevel, based_on, imapInfo); + + imapInfo->map_account = NULL; +} + +static void +check_online_id (ImapDialog *imap_dialog, GtkTreeIter *toplevel, Account *acc, GncImapInfo *imapInfo) +{ + // Check for online_id + gchar *head = "online_id"; + gchar *text = gnc_account_get_map_entry (acc, head, NULL); + + if (!text) + return; + + // Save source account + imapInfo->source_account = acc; + imapInfo->head = head; + + if (text && *text) + imapInfo->map_account = imapInfo->source_account; + + imapInfo->match_string = text; + + add_online_entry_to_store (imap_dialog, toplevel, _("Online Id"), imapInfo); + + g_free (text); +} + +static void +check_hbci (ImapDialog *imap_dialog, GtkTreeIter *toplevel, Account *acc, GncImapInfo *imapInfo) +{ + // Check for aqbanking hbci + gchar *head = "hbci"; + gchar *hbci_account_id = gnc_account_get_map_entry (acc, head, "account-id"); + gchar *hbci_bank_code = gnc_account_get_map_entry (acc, head, "bank-code"); + + if (!hbci_account_id || !hbci_bank_code) + return; + + gchar *text = g_strconcat (hbci_bank_code, ",", hbci_account_id, NULL); + + // Save source account + imapInfo->source_account = acc; + imapInfo->head = head; + + if (text && *text) + imapInfo->map_account = imapInfo->source_account; + + imapInfo->match_string = text; + + add_online_entry_to_store (imap_dialog, toplevel, _("Online HBCI"), imapInfo); + + g_free (hbci_account_id); + g_free (hbci_bank_code); + g_free (text); +} + +static void +check_ofx_account (ImapDialog *imap_dialog, GtkTreeIter *toplevel, Account *acc, GncImapInfo *imapInfo) +{ + // Check for ofx income account + gchar *head = "ofx/associated-income-account"; + GncGUID *acct_guid = gnc_account_get_map_guid_entry (acc, head, NULL); + + if (!acct_guid) + return; + + gchar *text = guid_to_string (acct_guid); + + // Save source account + imapInfo->source_account = acc; + imapInfo->head = head; + + if (text && *text) + imapInfo->map_account = xaccAccountLookup (acct_guid, gnc_get_current_book ()); + + imapInfo->match_string = text; + + add_online_entry_to_store (imap_dialog, toplevel, _("OFX Income Account"), imapInfo); + + guid_free (acct_guid); + g_free (text); +} + static void get_account_info_online (ImapDialog *imap_dialog, GList *accts) { @@ -711,65 +802,24 @@ get_account_info_online (ImapDialog *imap_dialog, GList *accts) GncImapInfo imapInfo; + imapInfo.map_account = NULL; + imapInfo.source_account = NULL; + imapInfo.category = " "; + imapInfo.count = " "; + /* Go through list of accounts */ for (ptr = accts; ptr; ptr = g_list_next (ptr)) { - gchar *hbci_account_id = NULL; - gchar *hbci_bank_code = NULL; - gchar *text = NULL; Account *acc = ptr->data; // Check for online_id - text = gnc_account_get_map_entry (acc, "online_id", NULL); - - if (text != NULL) - { - // Save source account - imapInfo.source_account = acc; - imapInfo.head = "online_id"; - imapInfo.category = " "; - - if (g_strcmp0 (text, "") == 0) - imapInfo.map_account = NULL; - else - imapInfo.map_account = imapInfo.source_account; - - imapInfo.match_string = text; - imapInfo.count = " "; - - // Add top level entry and pass iter to add_to_store - gtk_tree_store_append (GTK_TREE_STORE(imap_dialog->model), &toplevel, NULL); - add_to_store (imap_dialog, &toplevel, _("Online Id"), &imapInfo); - } - g_free (text); + check_online_id (imap_dialog, &toplevel, acc, &imapInfo); // Check for aqbanking hbci - hbci_account_id = gnc_account_get_map_entry (acc, "hbci", "account-id"); - hbci_bank_code = gnc_account_get_map_entry (acc, "hbci", "bank-code"); - text = g_strconcat (hbci_bank_code, ",", hbci_account_id, NULL); + check_hbci (imap_dialog, &toplevel, acc, &imapInfo); - if ((hbci_account_id != NULL) || (hbci_bank_code != NULL)) - { - // Save source account - imapInfo.source_account = acc; - imapInfo.head = "hbci"; - imapInfo.category = " "; - - if (g_strcmp0 (text, "") == 0) - imapInfo.map_account = NULL; - else - imapInfo.map_account = imapInfo.source_account; - - imapInfo.match_string = text; - imapInfo.count = " "; - - // Add top level entry and pass iter to add_to_store - gtk_tree_store_append (GTK_TREE_STORE(imap_dialog->model), &toplevel, NULL); - add_to_store (imap_dialog, &toplevel, _("Online HBCI"), &imapInfo); - } - g_free (hbci_account_id); - g_free (hbci_bank_code); - g_free (text); + // Check for ofx income account + check_ofx_account (imap_dialog, &toplevel, acc, &imapInfo); } } diff --git a/libgnucash/engine/Account.cpp b/libgnucash/engine/Account.cpp index e6ea9f640d..019c613a53 100644 --- a/libgnucash/engine/Account.cpp +++ b/libgnucash/engine/Account.cpp @@ -2583,6 +2583,13 @@ get_kvp_int64_path (const Account *acc, const Path& path) return qof_instance_get_path_kvp (QOF_INSTANCE(acc), path); } +static GncGUID* +get_kvp_guid_path (const Account *acc, const Path& path) +{ + auto val{qof_instance_get_path_kvp (QOF_INSTANCE(acc), path)}; + return val ? guid_copy(*val) : nullptr; +} + void xaccAccountSetColor (Account *acc, const char *str) { @@ -5688,6 +5695,13 @@ gnc_account_get_map_entry (Account *acc, const char *head, const char *category) get_kvp_string_path (acc, {head})); } +GncGUID * +gnc_account_get_map_guid_entry (Account *acc, const char *head, const char *category) +{ + return category ? + get_kvp_guid_path (acc, {head, category}) : + get_kvp_guid_path (acc, {head}); +} void gnc_account_delete_map_entry (Account *acc, char *head, char *category, diff --git a/libgnucash/engine/Account.h b/libgnucash/engine/Account.h index f748588735..0043d6a410 100644 --- a/libgnucash/engine/Account.h +++ b/libgnucash/engine/Account.h @@ -1648,6 +1648,11 @@ typedef enum */ gchar *gnc_account_get_map_entry (Account *acc, const char *head, const char *category); + /** Returns the guid pointed to by head and category for the Account, free + * the returned guid + */ + GncGUID *gnc_account_get_map_guid_entry (Account *acc, const char *head, const char *category); + /** Delete the entry for Account pointed to by head,category and match_string, * if empty is TRUE then use delete if empty */ diff --git a/libgnucash/engine/test/utest-Account.cpp b/libgnucash/engine/test/utest-Account.cpp index da8d240b63..fc52032ba1 100644 --- a/libgnucash/engine/test/utest-Account.cpp +++ b/libgnucash/engine/test/utest-Account.cpp @@ -1366,6 +1366,30 @@ test_gnc_account_get_map_entry (Fixture *fixture, gconstpointer pData) xaccAccountDestroy (account); } +static void +test_gnc_account_get_map_guid_entry (Fixture *fixture, gconstpointer pData) +{ + Account *account = xaccMallocAccount (gnc_account_get_book (fixture->acct)); + const GncGUID *account_guid = xaccAccountGetGUID (account); + + xaccAccountBeginEdit (fixture->acct); + qof_instance_set (QOF_INSTANCE(fixture->acct), "ofx-income-account", account_guid, nullptr); + xaccAccountCommitEdit (fixture->acct); + + GncGUID *guid = gnc_account_get_map_guid_entry (fixture->acct, "ofx/associated-income-account", nullptr); + g_assert_true (guid_equal (account_guid, guid)); + guid_free (guid); + + gnc_account_delete_map_entry (fixture->acct, "ofx/associated-income-account", nullptr, nullptr, false); + + guid = gnc_account_get_map_guid_entry (fixture->acct, "ofx/associated-income-account", nullptr); + g_assert_true (!guid); + guid_free (guid); + + xaccAccountBeginEdit (account); + xaccAccountDestroy (account); +} + static void test_gnc_account_insert_remove_split (Fixture *fixture, gconstpointer pData) { @@ -2869,6 +2893,7 @@ test_suite_account (void) // GNC_TEST_ADD (suitename, "xaccAccountEqual", Fixture, NULL, setup, test_xaccAccountEqual, teardown ); GNC_TEST_ADD (suitename, "gnc account kvp getters & setters", Fixture, NULL, setup, test_gnc_account_kvp_setters_getters, teardown ); GNC_TEST_ADD (suitename, "test_gnc_account_get_map_entry", Fixture, NULL, setup, test_gnc_account_get_map_entry, teardown ); + GNC_TEST_ADD (suitename, "test_gnc_account_get_map_guid_entry", Fixture, NULL, setup, test_gnc_account_get_map_guid_entry, teardown ); GNC_TEST_ADD (suitename, "gnc account insert & remove split", Fixture, NULL, setup, test_gnc_account_insert_remove_split, teardown ); GNC_TEST_ADD (suitename, "xaccAccount Insert and Remove Lot", Fixture, &good_data, setup, test_xaccAccountInsertRemoveLot, teardown ); GNC_TEST_ADD (suitename, "xaccAccountRecomputeBalance", Fixture, &some_data, setup, test_xaccAccountRecomputeBalance, teardown );