diff --git a/libgnucash/engine/Scrub.c b/libgnucash/engine/Scrub.c index 79be580a95..ce1c074e73 100644 --- a/libgnucash/engine/Scrub.c +++ b/libgnucash/engine/Scrub.c @@ -53,6 +53,8 @@ #include "gnc-commodity.h" #include "qofinstance-p.h" #include "gnc-session.h" +#include "qofquery.h" +#include "Query.h" #undef G_LOG_DOMAIN #define G_LOG_DOMAIN "gnc.engine.scrub" @@ -89,47 +91,43 @@ gnc_get_ongoing_scrub (void) /* ================================================================ */ -void -xaccAccountTreeScrubOrphans (Account *acc, QofPercentageFunc percentagefunc) +static GList* +get_all_transactions (Account *account, bool descendants) { - if (!acc) return; - - if (abort_now) - (percentagefunc)(NULL, -1.0); - - scrub_depth ++; - xaccAccountScrubOrphans (acc, percentagefunc); - gnc_account_foreach_descendant(acc, - (AccountCb)xaccAccountScrubOrphans, percentagefunc); - scrub_depth--; + GList *accounts = descendants ? gnc_account_get_descendants (account) : NULL; + accounts = g_list_prepend (accounts, account); + QofQuery *q = qof_query_create_for (GNC_ID_TRANS); + QofBook *book = qof_session_get_book (gnc_get_current_session ()); + qof_query_set_book (q, book); + xaccQueryAddAccountMatch (q, accounts, QOF_GUID_MATCH_ANY, QOF_QUERY_AND); + GList *transactions = g_list_copy (qof_query_run (q)); + qof_query_destroy (q); + return transactions; } +/* ================================================================ */ + static void TransScrubOrphansFast (Transaction *trans, Account *root) { - GList *node; - gchar *accname; + g_return_if_fail (trans && trans->common_currency && root); - if (!trans) return; - g_return_if_fail (root); - g_return_if_fail (trans->common_currency); - - for (node = trans->splits; node; node = node->next) + for (GList *node = trans->splits; node; node = node->next) { Split *split = node->data; - Account *orph; if (abort_now) break; if (split->acc) continue; DEBUG ("Found an orphan\n"); - accname = g_strconcat (_("Orphan"), "-", - gnc_commodity_get_mnemonic (trans->common_currency), - NULL); - orph = xaccScrubUtilityGetOrMakeAccount (root, trans->common_currency, - accname, ACCT_TYPE_BANK, - FALSE, TRUE); + gchar *accname = g_strconcat + (_("Orphan"), "-", gnc_commodity_get_mnemonic (trans->common_currency), + NULL); + + Account *orph = xaccScrubUtilityGetOrMakeAccount + (root, trans->common_currency, accname, ACCT_TYPE_BANK, false, true); + g_free (accname); if (!orph) continue; @@ -137,43 +135,47 @@ TransScrubOrphansFast (Transaction *trans, Account *root) } } -void -xaccAccountScrubOrphans (Account *acc, QofPercentageFunc percentagefunc) +static void +AccountScrubOrphans (Account *acc, bool descendants, QofPercentageFunc percentagefunc) { - GList *node, *splits; - const char *str; - const char *message = _( "Looking for orphans in account %s: %u of %u"); - guint total_splits = 0; - guint current_split = 0; - if (!acc) return; scrub_depth++; - str = xaccAccountGetName (acc); - str = str ? str : "(null)"; - PINFO ("Looking for orphans in account %s\n", str); - splits = xaccAccountGetSplitList(acc); - total_splits = g_list_length (splits); + GList *transactions = get_all_transactions (acc, descendants); + gint total_trans = g_list_length (transactions); + const char *message = _( "Looking for orphans in transaction: %u of %u"); + guint current_trans = 0; - for (node = splits; node; node = node->next) + for (GList *node = transactions; node; current_trans++, node = node->next) { - Split *split = node->data; - if (current_split % 10 == 0) + Transaction *trans = node->data; + if (current_trans % 10 == 0) { - char *progress_msg = g_strdup_printf (message, str, current_split, total_splits); - (percentagefunc)(progress_msg, (100 * current_split) / total_splits); + char *progress_msg = g_strdup_printf (message, current_trans, total_trans); + (percentagefunc)(progress_msg, (100 * current_trans) / total_trans); g_free (progress_msg); if (abort_now) break; } - TransScrubOrphansFast (xaccSplitGetParent (split), - gnc_account_get_root (acc)); - current_split++; + TransScrubOrphansFast (trans, gnc_account_get_root (acc)); } (percentagefunc)(NULL, -1.0); scrub_depth--; + + g_list_free (transactions); } +void +xaccAccountScrubOrphans (Account *acc, QofPercentageFunc percentagefunc) +{ + AccountScrubOrphans (acc, false, percentagefunc); +} + +void +xaccAccountTreeScrubOrphans (Account *acc, QofPercentageFunc percentagefunc) +{ + AccountScrubOrphans (acc, true, percentagefunc); +} void xaccTransScrubOrphans (Transaction *trans) @@ -350,6 +352,49 @@ split_scrub_or_dry_run (Split *split, bool dry_run) /* ================================================================ */ + +static void +AccountScrubImbalance (Account *acc, bool descendants, + QofPercentageFunc percentagefunc) +{ + const char *message = _( "Looking for imbalances in transaction date %s: %u of %u"); + + if (!acc) return; + + QofBook *book = qof_session_get_book (gnc_get_current_session ()); + Account *root = gnc_book_get_root_account (book); + GList *transactions = get_all_transactions (acc, descendants); + guint count = g_list_length (transactions), curr_trans = 0; + + scrub_depth++; + for (GList *node = transactions; node; node = node->next, curr_trans++) + { + Transaction *trans = node->data; + if (abort_now) break; + + PINFO("Start processing transaction %d of %d", curr_trans + 1, count); + + if (curr_trans % 10 == 0) + { + char *date = qof_print_date (xaccTransGetDate (trans)); + char *progress_msg = g_strdup_printf (message, date, curr_trans, count); + (percentagefunc)(progress_msg, (100 * curr_trans) / count); + g_free (progress_msg); + g_free (date); + } + + TransScrubOrphansFast (trans, root); + xaccTransScrubCurrency(trans); + xaccTransScrubImbalance (trans, root, NULL); + + PINFO("Finished processing transaction %d of %d", curr_trans + 1, count); + } + (percentagefunc)(NULL, -1.0); + scrub_depth--; + + g_list_free (transactions); +} + void xaccTransScrubSplits (Transaction *trans) { @@ -391,75 +436,13 @@ xaccSplitScrub (Split *split) void xaccAccountTreeScrubImbalance (Account *acc, QofPercentageFunc percentagefunc) { - if (!acc) return; - - if (abort_now) - (percentagefunc)(NULL, -1.0); - - scrub_depth++; - xaccAccountScrubImbalance (acc, percentagefunc); - gnc_account_foreach_descendant(acc, - (AccountCb)xaccAccountScrubImbalance, percentagefunc); - scrub_depth--; + AccountScrubImbalance (acc, true, percentagefunc); } void xaccAccountScrubImbalance (Account *acc, QofPercentageFunc percentagefunc) { - GList *node, *splits; - const char *str; - const char *message = _( "Looking for imbalances in account %s: %u of %u"); - gint split_count = 0, curr_split_no = 0; - - if (!acc) return; - /* If it's a trading account and an imbalanced transaction is - * found the trading splits will be replaced, invalidating the - * split list in mid-traversal, see - * https://bugs.gnucash.org/show_bug.cgi?id=798346. Also the - * transactions will get scrubbed at least twice from their "real" - * accounts anyway so doing so from the trading accounts is wasted - * effort. - */ - if (xaccAccountGetType(acc) == ACCT_TYPE_TRADING) - return; - - scrub_depth++; - - str = xaccAccountGetName(acc); - str = str ? str : "(null)"; - PINFO ("Looking for imbalances in account %s\n", str); - - splits = xaccAccountGetSplitList(acc); - split_count = g_list_length (splits); - for (node = splits; node; node = node->next) - { - Split *split = node->data; - Transaction *trans = xaccSplitGetParent(split); - if (abort_now) break; - - PINFO("Start processing split %d of %d", - curr_split_no + 1, split_count); - - if (curr_split_no % 10 == 0) - { - char *progress_msg = g_strdup_printf (message, str, curr_split_no, split_count); - (percentagefunc)(progress_msg, (100 * curr_split_no) / split_count); - g_free (progress_msg); - } - - TransScrubOrphansFast (xaccSplitGetParent (split), - gnc_account_get_root (acc)); - - xaccTransScrubCurrency(trans); - - xaccTransScrubImbalance (trans, gnc_account_get_root (acc), NULL); - - PINFO("Finished processing split %d of %d", - curr_split_no + 1, split_count); - curr_split_no++; - } - (percentagefunc)(NULL, -1.0); - scrub_depth--; + AccountScrubImbalance (acc, false, percentagefunc); } static Split *