From 0f8d9f6d7cadfcc40cec4efa5d6f370a739ae817 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 26 Jan 2021 16:16:25 -0800 Subject: [PATCH] Bug 798093 - Changing the symbol/abbreviation of a security after... the trading account was created breaks GnuCash. Introduces a new function, gnc_account_lookup_by_type_and_commodity that does that, though it also looks at name for the one special case of finding/creating the Namespace placeholder account. Adds a parameter checkname to xaccScrubUtilityGetOrMakeAccount to flag whether to look for the name. Namespaces aside this makes it possible for the user to rename trading accounts or securities independent of each other. --- libgnucash/engine/Account.cpp | 22 ++++++++++++++++++++++ libgnucash/engine/Account.h | 18 ++++++++++++++++++ libgnucash/engine/Scrub.c | 24 ++++++++++++++++-------- 3 files changed, 56 insertions(+), 8 deletions(-) diff --git a/libgnucash/engine/Account.cpp b/libgnucash/engine/Account.cpp index 090e7d8370..718f5deba6 100644 --- a/libgnucash/engine/Account.cpp +++ b/libgnucash/engine/Account.cpp @@ -3120,6 +3120,28 @@ gnc_account_lookup_by_full_name (const Account *any_acc, return found; } +Account* +gnc_account_lookup_by_type_and_commodity (Account* root, + const char* name, + GNCAccountType acctype, + gnc_commodity* commodity) +{ + auto rpriv{GET_PRIVATE(root)}; + for (auto node = rpriv->children; node; node = node->next) + { + auto account{static_cast(node->data)}; + if (xaccAccountGetType (account) == acctype && + gnc_commodity_equiv(xaccAccountGetCommodity (account), commodity)) + { + if (name && strcmp(name, xaccAccountGetName(account))) + continue; //name doesn't match so skip this one + + return account; + } + } + return nullptr; +} + void gnc_account_foreach_child (const Account *acc, AccountCb thunk, diff --git a/libgnucash/engine/Account.h b/libgnucash/engine/Account.h index 78eff72448..22c02f0765 100644 --- a/libgnucash/engine/Account.h +++ b/libgnucash/engine/Account.h @@ -938,6 +938,24 @@ Account *gnc_account_lookup_by_code (const Account *parent, */ Account *gnc_account_lookup_by_opening_balance (Account *account, gnc_commodity *commodity); +/** Find a direct child account matching name, GNCAccountType, and commodity. + * + * Note that commodity matching is by equivalence: If the + * mnemonic/symbol and namespace are the same, it matches. + * + * @param root The account among whose children one expects to find + * the account. + * @param name The name of the account to look for. If nullptr the + * returned account will match only on acctype and commodity. + * @param acctype The GNCAccountType to match. + * @param commodity The commodity in which the account should be denominated. + * @return The book's trading account for the given commodity if + * trading accounts are enabled and one exists; NULL otherwise. + */ +Account *gnc_account_lookup_by_type_and_commodity (Account* root, + const char* name, + GNCAccountType acctype, + gnc_commodity* commodity); /** @} */ /* ------------------ */ diff --git a/libgnucash/engine/Scrub.c b/libgnucash/engine/Scrub.c index 3965ab1b0d..0c77f9dd56 100644 --- a/libgnucash/engine/Scrub.c +++ b/libgnucash/engine/Scrub.c @@ -64,7 +64,8 @@ static Account* xaccScrubUtilityGetOrMakeAccount (Account *root, gnc_commodity* currency, const char* accname, GNCAccountType acctype, - gboolean placeholder); + gboolean placeholder, + gboolean checkname); void gnc_set_abort_scrub (gboolean abort) @@ -124,7 +125,8 @@ TransScrubOrphansFast (Transaction *trans, Account *root) gnc_commodity_get_mnemonic (trans->common_currency), NULL); orph = xaccScrubUtilityGetOrMakeAccount (root, trans->common_currency, - accname, ACCT_TYPE_BANK, FALSE); + accname, ACCT_TYPE_BANK, + FALSE, TRUE); g_free (accname); if (!orph) continue; @@ -413,7 +415,8 @@ get_balance_split (Transaction *trans, Account *root, Account *account, accname = g_strconcat (_("Imbalance"), "-", gnc_commodity_get_mnemonic (commodity), NULL); account = xaccScrubUtilityGetOrMakeAccount (root, commodity, - accname, ACCT_TYPE_BANK, FALSE); + accname, ACCT_TYPE_BANK, + FALSE, TRUE); g_free (accname); if (!account) { @@ -476,7 +479,8 @@ get_trading_split (Transaction *trans, Account *root, trading_account = xaccScrubUtilityGetOrMakeAccount (root, default_currency, _("Trading"), - ACCT_TYPE_TRADING, TRUE); + ACCT_TYPE_TRADING, + TRUE, FALSE); if (!trading_account) { PERR ("Can't get trading account"); @@ -486,7 +490,8 @@ get_trading_split (Transaction *trans, Account *root, ns_account = xaccScrubUtilityGetOrMakeAccount (trading_account, default_currency, gnc_commodity_get_namespace(commodity), - ACCT_TYPE_TRADING, TRUE); + ACCT_TYPE_TRADING, + TRUE, TRUE); if (!ns_account) { PERR ("Can't get namespace account"); @@ -495,7 +500,8 @@ get_trading_split (Transaction *trans, Account *root, account = xaccScrubUtilityGetOrMakeAccount (ns_account, commodity, gnc_commodity_get_mnemonic(commodity), - ACCT_TYPE_TRADING, FALSE); + ACCT_TYPE_TRADING, + FALSE, FALSE); if (!account) { PERR ("Can't get commodity account"); @@ -1447,7 +1453,7 @@ xaccAccountScrubColorNotSet (QofBook *book) Account * xaccScrubUtilityGetOrMakeAccount (Account *root, gnc_commodity * currency, const char *accname, GNCAccountType acctype, - gboolean placeholder) + gboolean placeholder, gboolean checkname) { Account * acc; @@ -1461,7 +1467,9 @@ xaccScrubUtilityGetOrMakeAccount (Account *root, gnc_commodity * currency, } /* See if we've got one of these going already ... */ - acc = gnc_account_lookup_by_name(root, accname); + acc = gnc_account_lookup_by_type_and_commodity (root, + checkname ? accname : NULL, + acctype, currency); if (acc == NULL) {