diff --git a/gnucash/gnome-utils/dialog-account.c b/gnucash/gnome-utils/dialog-account.c index 41b2832f22..bb7e0746b2 100644 --- a/gnucash/gnome-utils/dialog-account.c +++ b/gnucash/gnome-utils/dialog-account.c @@ -247,7 +247,7 @@ gnc_account_opening_balance_button_update (AccountWindow *aw, gnc_commodity *com { Account *account = aw_get_account (aw); Account *ob_account = gnc_account_lookup_by_opening_balance (gnc_book_get_root_account (aw->book), commodity); - gboolean has_splits = (xaccAccountGetSplitList (account) != NULL); + gboolean has_splits = (xaccAccountGetSplitsSize (account) != 0); if (aw->type != ACCT_TYPE_EQUITY) { @@ -1614,7 +1614,7 @@ gnc_account_window_create (GtkWindow *parent, AccountWindow *aw) gtk_box_pack_start (GTK_BOX(box), aw->commodity_edit, TRUE, TRUE, 0); gtk_widget_show (aw->commodity_edit); // If the account has transactions, prevent changes by displaying a label and tooltip - if (xaccAccountGetSplitList (aw_get_account (aw)) != NULL) + if (xaccAccountGetSplitsSize (aw_get_account (aw)) != 0) { gtk_widget_set_tooltip_text (aw->commodity_edit, tt); gtk_widget_set_sensitive (aw->commodity_edit, FALSE); @@ -1714,7 +1714,7 @@ gnc_account_window_create (GtkWindow *parent, AccountWindow *aw) // immutable if gnucash depends on details that would be lost/missing // if changing from/to such a type. At the time of this writing the // immutable types are AR, AP and trading types. - if (xaccAccountGetSplitList (aw_get_account (aw)) != NULL) + if (xaccAccountGetSplitsSize (aw_get_account (aw)) != 0) { GNCAccountType atype = xaccAccountGetType (aw_get_account (aw)); compat_types = xaccAccountTypesCompatibleWith (atype); @@ -2128,7 +2128,7 @@ gnc_ui_edit_account_window (GtkWindow *parent, Account *account) gnc_resume_gui_refresh (); gtk_widget_show_all (aw->dialog); - if (xaccAccountGetSplitList (account) != NULL) + if (xaccAccountGetSplitsSize (account) != 0) gtk_widget_hide (aw->opening_balance_page); parent_acct = gnc_account_get_parent (account); diff --git a/gnucash/gnome-utils/dialog-transfer.cpp b/gnucash/gnome-utils/dialog-transfer.cpp index f1d3acf048..8ed87051bf 100644 --- a/gnucash/gnome-utils/dialog-transfer.cpp +++ b/gnucash/gnome-utils/dialog-transfer.cpp @@ -44,6 +44,7 @@ #include "gnc-ui.h" #include "Transaction.h" #include "Account.h" +#include "Account.hpp" #include "engine-helpers.h" #include "QuickFill.h" #include @@ -472,10 +473,8 @@ gnc_xfer_dialog_reload_quickfill( XferDialog *xferData ) gnc_quickfill_destroy( xferData->qf ); xferData->qf = gnc_quickfill_new(); - auto splitlist = xaccAccountGetSplitList( account ); - for ( GList *node = splitlist; node; node = node->next ) + for (auto split : xaccAccountGetSplits (account)) { - auto split = static_cast (node->data); auto trans = xaccSplitGetParent (split); gnc_quickfill_insert( xferData->qf, xaccTransGetDescription (trans), QUICKFILL_LIFO); diff --git a/gnucash/gnome-utils/gnc-autoclear.c b/gnucash/gnome-utils/gnc-autoclear.c index 0b4ff9249d..b6e4120bad 100644 --- a/gnucash/gnome-utils/gnc-autoclear.c +++ b/gnucash/gnome-utils/gnc-autoclear.c @@ -143,6 +143,7 @@ gnc_autoclear_get_splits (Account *account, gnc_numeric toclear_value, else nc_list = g_list_prepend (nc_list, split); } + g_list_free (acc_splits); if (gnc_numeric_zero_p (toclear_value)) { diff --git a/gnucash/gnome-utils/gnc-file.c b/gnucash/gnome-utils/gnc-file.c index 71e66165ae..cea2cc7f44 100644 --- a/gnucash/gnome-utils/gnc-file.c +++ b/gnucash/gnome-utils/gnc-file.c @@ -1094,6 +1094,7 @@ RESTART: GList *splits = xaccAccountGetSplitList (acc); g_list_foreach (splits, (GFunc)gnc_sx_scrub_split_numerics, NULL); + g_list_free (splits); } g_list_free (children); } diff --git a/gnucash/gnome/assistant-stock-transaction.cpp b/gnucash/gnome/assistant-stock-transaction.cpp index 1b26ac18ab..5f7de1d349 100644 --- a/gnucash/gnome/assistant-stock-transaction.cpp +++ b/gnucash/gnome/assistant-stock-transaction.cpp @@ -37,6 +37,7 @@ #include #include "Account.h" +#include "Account.hpp" #include "Transaction.h" #include "engine-helpers.h" #include "dialog-utils.h" @@ -1262,9 +1263,8 @@ StockAssistantModel::set_txn_type (guint type_idx) }; static void -check_txn_date(GList* last_split_node, time64 txn_date, Logger& logger) +check_txn_date(Split* last_split, time64 txn_date, Logger& logger) { - auto last_split = static_cast(last_split_node->data); auto last_split_date = xaccTransGetDate(xaccSplitGetParent(last_split)); if (txn_date <= last_split_date) { auto last_split_date_str = qof_print_date(last_split_date); @@ -1301,9 +1301,9 @@ StockAssistantModel::generate_list_of_splits() { // transactions dated after the date specified, it is very likely // the later stock transactions will be invalidated. warn the user // to review them. - auto last_split_node = g_list_last (xaccAccountGetSplitList (m_acct)); - if (last_split_node) - check_txn_date(last_split_node, m_transaction_date, m_logger); + auto splits{xaccAccountGetSplits (m_acct)}; + if (!splits.empty()) + check_txn_date(splits.back(), m_transaction_date, m_logger); if (m_stock_entry->enabled() || m_stock_entry->has_amount()) { diff --git a/gnucash/gnome/dialog-lot-viewer.c b/gnucash/gnome/dialog-lot-viewer.c index 2856186731..cf1d4a69ca 100644 --- a/gnucash/gnome/dialog-lot-viewer.c +++ b/gnucash/gnome/dialog-lot-viewer.c @@ -236,6 +236,7 @@ lv_show_splits_free (GNCLotViewer *lv) /* display list */ gnc_split_viewer_fill(lv, lv->split_free_store, filtered_list); g_list_free (filtered_list); + g_list_free (split_list); } /* ======================================================================== */ diff --git a/gnucash/gnome/dialog-sx-editor.c b/gnucash/gnome/dialog-sx-editor.c index a85b84ab4c..4c1a54ee91 100644 --- a/gnucash/gnome/dialog-sx-editor.c +++ b/gnucash/gnome/dialog-sx-editor.c @@ -847,6 +847,7 @@ gnc_sxed_check_consistent (GncSxEditorDialog *sxed) g_hash_table_foreach (txns, set_sums_to_zero, NULL); splitCount += g_list_length (splitList); + g_list_free (splitList); xaccAccountForEachTransaction (tmpl_acct, check_transaction_splits, &sd); @@ -1510,6 +1511,7 @@ schedXact_editor_populate (GncSxEditorDialog *sxed) splitReg = gnc_ledger_display_get_split_register (sxed->ledger); gnc_split_register_load (splitReg, splitList, NULL); } /* otherwise, use the existing stuff. */ + g_list_free (splitList); } /* Update the example cal */ diff --git a/gnucash/gnome/gnc-plugin-page-account-tree.cpp b/gnucash/gnome/gnc-plugin-page-account-tree.cpp index ffb3bc2f15..15f764104b 100644 --- a/gnucash/gnome/gnc-plugin-page-account-tree.cpp +++ b/gnucash/gnome/gnc-plugin-page-account-tree.cpp @@ -39,6 +39,7 @@ #include "gnc-plugin-page-account-tree.h" #include "gnc-plugin-page-register.h" +#include "Account.hpp" #include "Scrub.h" #include "Scrub3.h" #include "ScrubBusiness.h" @@ -1145,22 +1146,19 @@ static gpointer delete_account_helper (Account * account, gpointer data) { auto helper_res = static_cast(data); - GList *splits; + auto splits{xaccAccountGetSplits (account)}; - splits = xaccAccountGetSplitList (account); - if (splits) + if (!splits.empty()) { helper_res->has_splits = TRUE; - while (splits) + for (auto s : splits) { - auto s = GNC_SPLIT(splits->data); Transaction *txn = xaccSplitGetParent (s); if (xaccTransGetReadOnly (txn)) { helper_res->has_ro_splits = TRUE; break; } - splits = splits->next; } } @@ -1383,7 +1381,6 @@ account_delete_dialog (Account *account, GtkWindow *parent, Adopters* adopt) gchar *title = NULL; GtkBuilder *builder = gtk_builder_new(); gchar *acct_name = gnc_account_get_full_name(account); - GList* splits = xaccAccountGetSplitList(account); GList* filter = g_list_prepend(NULL, (gpointer)xaccAccountGetType(account)); if (!acct_name) @@ -1416,7 +1413,7 @@ account_delete_dialog (Account *account, GtkWindow *parent, Adopters* adopt) account, FALSE); // Does the selected account have splits - if (splits) + if (!xaccAccountGetSplits(account).empty()) { delete_helper_t delete_res2 = { FALSE, FALSE }; @@ -1537,8 +1534,7 @@ gnc_plugin_page_account_tree_cmd_delete_account (GSimpleAction *simple, } // If no transaction or children just delete it. - if (!(xaccAccountGetSplitList (account) != NULL || - gnc_account_n_children (account))) + if (xaccAccountGetSplits (account).empty() && gnc_account_n_children (account) == 0) { do_delete_account (account, NULL, NULL, NULL); return; @@ -1581,7 +1577,6 @@ confirm_delete_account (GSimpleAction *simple, GncPluginPageAccountTree *page, delete_helper_t delete_res) { Account *account = gnc_plugin_page_account_tree_get_current_account (page); - GList* splits = xaccAccountGetSplitList(account); GtkWidget* window = gnc_plugin_page_get_window(GNC_PLUGIN_PAGE(page)); gint response; @@ -1595,7 +1590,7 @@ confirm_delete_account (GSimpleAction *simple, GncPluginPageAccountTree *page, acct_name); g_free(acct_name); - if (splits) + if (!xaccAccountGetSplits (account).empty()) { if (ta) { diff --git a/gnucash/gnome/test/gtest-assistant-stock-transaction.cpp b/gnucash/gnome/test/gtest-assistant-stock-transaction.cpp index f328a324d1..3758ccba32 100644 --- a/gnucash/gnome/test/gtest-assistant-stock-transaction.cpp +++ b/gnucash/gnome/test/gtest-assistant-stock-transaction.cpp @@ -231,9 +231,8 @@ static void dump_acct (Account *acct) std::cout << '\n' << std::setw(20) << std::right << xaccAccountGetName (acct) << " Bal=" << std::setw(10) << std::right << GncNumeric (xaccAccountGetBalance (acct)) << std::endl; - for (auto n = xaccAccountGetSplitList (acct); n; n = n->next) + for (auto s : xaccAccountGetSplits (acct)) { - auto s = static_cast(n->data); bal += xaccSplitGetAmount (s); std::cout << std::setw(20) << std::right << GncDateTime (xaccTransGetDate (xaccSplitGetParent (s))).format_iso8601() << " amt=" << std::setw(10) << std::right << GncNumeric (xaccSplitGetAmount (s)) diff --git a/gnucash/gnome/window-reconcile.cpp b/gnucash/gnome/window-reconcile.cpp index 1d352f05d8..8b4a96b0d7 100644 --- a/gnucash/gnome/window-reconcile.cpp +++ b/gnucash/gnome/window-reconcile.cpp @@ -37,6 +37,7 @@ #endif #include +#include "Account.hpp" #include "Scrub.h" #include "Scrub3.h" #include "dialog-account.h" @@ -1621,10 +1622,8 @@ recn_set_watches_one_account (gpointer data, gpointer user_data) QOF_EVENT_MODIFY | QOF_EVENT_DESTROY); /* add a watch on each unreconciled or cleared split for the account */ - GList *splits = xaccAccountGetSplitList (account); - for (GList *node = splits; node; node = node->next) + for (auto split : xaccAccountGetSplits (account)) { - auto split = GNC_SPLIT(node->data); Transaction *trans; char recn; @@ -1942,22 +1941,16 @@ recnWindowWithBalance (GtkWidget *parent, Account *account, gnc_numeric new_endi GtkWidget *box = gtk_statusbar_get_message_area (bar); GtkWidget *image = gtk_image_new_from_icon_name ("dialog-warning", GTK_ICON_SIZE_SMALL_TOOLBAR); - GList *splits = xaccAccountGetSplitList (account); - for (GList *n = splits; n; n = n->next) + auto find_split = [statement_date](const Split *split) + { return (xaccSplitGetReconcile (split) == YREC && + xaccSplitGetDateReconciled (split) > statement_date); }; + + if (auto split = gnc_account_find_split (account, find_split, true)) { - auto split = GNC_SPLIT(n->data); - time64 recn_date = xaccSplitGetDateReconciled (split); - gchar *datestr, *recnstr; - if ((xaccSplitGetReconcile (split) != YREC) || - (recn_date <= statement_date)) - continue; - - datestr = qof_print_date (xaccTransGetDate (xaccSplitGetParent (split))); - recnstr = qof_print_date (recn_date); + auto datestr = qof_print_date (xaccTransGetDate (xaccSplitGetParent (split))); + auto recnstr = qof_print_date (xaccSplitGetDateReconciled (split)); PWARN ("split posting_date=%s, recn_date=%s", datestr, recnstr); - g_free (datestr); - g_free (recnstr); gtk_statusbar_push (bar, context, _("WARNING! Account contains \ splits whose reconcile date is after statement date. Reconciliation may be \ @@ -1970,7 +1963,9 @@ use Find Transactions to find them, unreconcile, and re-reconcile.")); gtk_box_pack_start (GTK_BOX(box), image, FALSE, FALSE, 0); gtk_box_reorder_child (GTK_BOX(box), image, 0); - break; + + g_free (datestr); + g_free (recnstr); } } @@ -2305,6 +2300,7 @@ find_payment_account(Account *account) } } + g_list_free (list); return rv; } diff --git a/gnucash/import-export/import-backend.cpp b/gnucash/import-export/import-backend.cpp index 5fbf9a082a..d333d7bfbb 100644 --- a/gnucash/import-export/import-backend.cpp +++ b/gnucash/import-export/import-backend.cpp @@ -38,6 +38,7 @@ #include "import-backend.h" #include "import-utilities.h" #include "Account.h" +#include "Account.hpp" #include "Query.h" #include "gnc-engine.h" #include "engine-helpers.h" @@ -996,9 +997,9 @@ hash_account_online_ids (Account *account) { auto acct_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, nullptr); - for (GList *n = xaccAccountGetSplitList (account) ; n; n = n->next) + for (auto split : xaccAccountGetSplits (account)) { - auto id = gnc_import_get_split_online_id (static_cast(n->data)); + auto id = gnc_import_get_split_online_id (split); if (id && *id) g_hash_table_insert (acct_hash, (void*) id, GINT_TO_POINTER (1)); } diff --git a/gnucash/import-export/import-main-matcher.cpp b/gnucash/import-export/import-main-matcher.cpp index 8f7c18b751..5acc3ba474 100644 --- a/gnucash/import-export/import-main-matcher.cpp +++ b/gnucash/import-export/import-main-matcher.cpp @@ -44,6 +44,7 @@ #include "import-main-matcher.h" +#include "Account.hpp" #include "dialog-transfer.h" #include "dialog-utils.h" #include "gnc-glib-utils.h" @@ -468,9 +469,8 @@ load_hash_tables (GNCImportMainMatcher *info) } for (GList *m = accounts_list; m; m = m->next) { - for (GList *n = xaccAccountGetSplitList (static_cast(m->data)); n; n = n->next) + for (auto s : xaccAccountGetSplits (static_cast(m->data))) { - auto s = static_cast(n->data); const Transaction *t = xaccSplitGetParent (s); const gchar *key = xaccTransGetDescription (t); diff --git a/gnucash/register/ledger-core/split-register-control.cpp b/gnucash/register/ledger-core/split-register-control.cpp index a1578868cd..c0a8c6519f 100644 --- a/gnucash/register/ledger-core/split-register-control.cpp +++ b/gnucash/register/ledger-core/split-register-control.cpp @@ -683,6 +683,7 @@ gnc_find_split_in_account_by_memo (Account *account, const char *memo, rv = split; } + g_list_free (splits); return rv; } diff --git a/libgnucash/engine/Account.cpp b/libgnucash/engine/Account.cpp index b4dbcb4b34..504e1794e2 100644 --- a/libgnucash/engine/Account.cpp +++ b/libgnucash/engine/Account.cpp @@ -34,6 +34,7 @@ #include #include "AccountP.hpp" +#include "Account.hpp" #include "Split.h" #include "Transaction.h" #include "TransactionP.h" @@ -331,7 +332,7 @@ gnc_account_init(Account* acc) priv->lower_balance_limit = {}; priv->include_sub_account_balances = {}; - priv->splits = nullptr; + priv->splits.clear(); priv->sort_dirty = FALSE; } @@ -1136,6 +1137,29 @@ xaccInitAccount (Account * acc, QofBook *book) /********************************************************************\ \********************************************************************/ +Split* +gnc_account_find_split (const Account *acc, std::function predicate, + bool reverse) +{ + if (!GNC_IS_ACCOUNT (acc)) + return nullptr; + + auto splits{GET_PRIVATE(acc)->splits}; + if (reverse) + { + auto latest = std::find_if(splits.rbegin(), splits.rend(), predicate); + return (latest == splits.rend()) ? nullptr : *latest; + } + else + { + auto earliest = std::find_if(splits.begin(), splits.end(), predicate); + return (earliest == splits.end()) ? nullptr : *earliest; + } +} + +/********************************************************************\ +\********************************************************************/ + QofBook * gnc_account_get_book(const Account *account) { @@ -1368,22 +1392,18 @@ xaccFreeAccount (Account *acc) /* NB there shouldn't be any splits by now ... they should * have been all been freed by CommitEdit(). We can remove this * check once we know the warning isn't occurring any more. */ - if (priv->splits) + if (!priv->splits.empty()) { - GList *slist; PERR (" instead of calling xaccFreeAccount(), please call\n" " xaccAccountBeginEdit(); xaccAccountDestroy();\n"); qof_instance_reset_editlevel(acc); - slist = g_list_copy(priv->splits); - for (lp = slist; lp; lp = lp->next) + for (auto s : priv->splits) { - Split *s = (Split *) lp->data; g_assert(xaccSplitGetAccount(s) == acc); xaccSplitDestroy (s); } - g_list_free(slist); /* Nothing here (or in xaccAccountCommitEdit) nullptrs priv->splits, so this asserts every time. g_assert(priv->splits == nullptr); */ @@ -1416,6 +1436,7 @@ xaccFreeAccount (Account *acc) priv->type = ACCT_TYPE_NONE; gnc_commodity_decrement_usage_count(priv->commodity); priv->commodity = nullptr; + priv->splits.clear(); priv->balance_dirty = FALSE; priv->sort_dirty = FALSE; @@ -1483,7 +1504,6 @@ xaccAccountCommitEdit (Account *acc) priv = GET_PRIVATE(acc); if (qof_instance_get_destroying(acc)) { - GList *lp, *slist; QofCollection *col; qof_instance_increase_editlevel(acc); @@ -1500,18 +1520,12 @@ xaccAccountCommitEdit (Account *acc) themselves will be destroyed by the transaction code */ if (!qof_book_shutting_down(book)) { - slist = g_list_copy(priv->splits); - for (lp = slist; lp; lp = lp->next) - { - Split *s = static_cast(lp->data); + for (auto s : priv->splits) xaccSplitDestroy (s); - } - g_list_free(slist); } else { - g_list_free(priv->splits); - priv->splits = nullptr; + priv->splits.clear(); } /* It turns out there's a case where this assertion does not hold: @@ -1528,7 +1542,7 @@ xaccAccountCommitEdit (Account *acc) qof_collection_foreach(col, destroy_pending_splits_for_account, acc); /* the lots should be empty by now */ - for (lp = priv->lots; lp; lp = lp->next) + for (auto lp = priv->lots; lp; lp = lp->next) { GNCLot *lot = static_cast(lp->data); gnc_lot_destroy (lot); @@ -1822,37 +1836,23 @@ xaccAccountEqual(const Account *aa, const Account *ab, gboolean check_guids) /* no parent; always compare downwards. */ { - GList *la = priv_aa->splits; - GList *lb = priv_ab->splits; - - if ((la && !lb) || (!la && lb)) + if (priv_aa->splits.size() != priv_ab->splits.size()) { - PWARN ("only one has splits"); - return FALSE; + PWARN ("number of splits differs"); + return false; } - if (la && lb) + for (auto it_a = priv_aa->splits.begin(), it_b = priv_ab->splits.begin(); + it_a != priv_aa->splits.end() && it_b != priv_ab->splits.end(); + ++it_a, ++it_b) { - /* presume that the splits are in the same order */ - while (la && lb) + Split *sa = *it_a; + Split *sb = *it_b; + + if (!xaccSplitEqual(sa, sb, check_guids, TRUE, FALSE)) { - Split *sa = (Split *) la->data; - Split *sb = (Split *) lb->data; - - if (!xaccSplitEqual(sa, sb, check_guids, TRUE, FALSE)) - { - PWARN ("splits differ"); - return(FALSE); - } - - la = la->next; - lb = lb->next; - } - - if ((la != nullptr) || (lb != nullptr)) - { - PWARN ("number of splits differs"); - return(FALSE); + PWARN ("splits differ"); + return false; } } } @@ -1922,30 +1922,29 @@ gboolean gnc_account_get_defer_bal_computation (Account *acc) /********************************************************************\ \********************************************************************/ +static bool split_cmp_less (const Split* a, const Split* b) +{ + return xaccSplitOrder (a, b) < 0; +} + gboolean gnc_account_insert_split (Account *acc, Split *s) { AccountPrivate *priv; - GList *node; g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE); g_return_val_if_fail(GNC_IS_SPLIT(s), FALSE); priv = GET_PRIVATE(acc); - node = g_list_find(priv->splits, s); - if (node) + if (std::find (priv->splits.begin(), priv->splits.end(), s) != priv->splits.end()) return FALSE; + priv->splits.push_back (s); + if (qof_instance_get_editlevel(acc) == 0) - { - priv->splits = g_list_insert_sorted(priv->splits, s, - (GCompareFunc)xaccSplitOrder); - } + std::sort (priv->splits.begin(), priv->splits.end(), split_cmp_less); else - { - priv->splits = g_list_prepend(priv->splits, s); - priv->sort_dirty = TRUE; - } + priv->sort_dirty = true; //FIXME: find better event qof_event_gen (&acc->inst, QOF_EVENT_MODIFY, nullptr); @@ -1962,17 +1961,17 @@ gboolean gnc_account_remove_split (Account *acc, Split *s) { AccountPrivate *priv; - GList *node; g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE); g_return_val_if_fail(GNC_IS_SPLIT(s), FALSE); priv = GET_PRIVATE(acc); - node = g_list_find(priv->splits, s); - if (nullptr == node) + + auto it = std::remove (priv->splits.begin(), priv->splits.end(), s); + if (it == priv->splits.end()) return FALSE; - priv->splits = g_list_delete_link(priv->splits, node); + priv->splits.erase (it, priv->splits.end()); //FIXME: find better event type qof_event_gen(&acc->inst, QOF_EVENT_MODIFY, nullptr); // And send the account-based event, too @@ -1993,7 +1992,7 @@ xaccAccountSortSplits (Account *acc, gboolean force) priv = GET_PRIVATE(acc); if (!priv->sort_dirty || (!force && qof_instance_get_editlevel(acc) > 0)) return; - priv->splits = g_list_sort(priv->splits, (GCompareFunc)xaccSplitOrder); + std::sort (priv->splits.begin(), priv->splits.end(), split_cmp_less); priv->sort_dirty = FALSE; priv->balance_dirty = TRUE; } @@ -2166,7 +2165,7 @@ xaccAccountInsertLot (Account *acc, GNCLot *lot) /********************************************************************\ \********************************************************************/ static void -xaccPreSplitMove (Split *split, gpointer dummy) +xaccPreSplitMove (Split *split) { xaccTransBeginEdit (xaccSplitGetParent (split)); } @@ -2193,7 +2192,7 @@ xaccAccountMoveAllSplits (Account *accfrom, Account *accto) /* optimizations */ from_priv = GET_PRIVATE(accfrom); - if (!from_priv->splits || accfrom == accto) + if (from_priv->splits.empty() || accfrom == accto) return; /* check for book mix-up */ @@ -2203,7 +2202,7 @@ xaccAccountMoveAllSplits (Account *accfrom, Account *accto) xaccAccountBeginEdit(accfrom); xaccAccountBeginEdit(accto); /* Begin editing both accounts and all transactions in accfrom. */ - g_list_foreach(from_priv->splits, (GFunc)xaccPreSplitMove, nullptr); + std::for_each (from_priv->splits.begin(), from_priv->splits.end(), xaccPreSplitMove); /* Concatenate accfrom's lists of splits and lots to accto's lists. */ //to_priv->splits = g_list_concat(to_priv->splits, from_priv->splits); @@ -2220,10 +2219,11 @@ xaccAccountMoveAllSplits (Account *accfrom, Account *accto) * Convert each split's amount to accto's commodity. * Commit to editing each transaction. */ - g_list_foreach(from_priv->splits, (GFunc)xaccPostSplitMove, (gpointer)accto); + std::for_each (from_priv->splits.begin(), from_priv->splits.end(), + [accto](Split *s){ xaccPostSplitMove (s, accto); }); /* Finally empty accfrom. */ - g_assert(from_priv->splits == nullptr); + g_assert(from_priv->splits.empty()); g_assert(from_priv->lots == nullptr); xaccAccountCommitEdit(accfrom); xaccAccountCommitEdit(accto); @@ -2268,7 +2268,6 @@ xaccAccountRecomputeBalance (Account * acc) gnc_numeric noclosing_balance; gnc_numeric cleared_balance; gnc_numeric reconciled_balance; - GList *lp; if (nullptr == acc) return; @@ -2285,9 +2284,8 @@ xaccAccountRecomputeBalance (Account * acc) PINFO ("acct=%s starting baln=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, priv->accountName, balance.num, balance.denom); - for (lp = priv->splits; lp; lp = lp->next) + for (auto split : priv->splits) { - Split *split = (Split *) lp->data; gnc_numeric amt = xaccSplitGetAmount (split); balance = gnc_numeric_add_fixed(balance, amt); @@ -2607,7 +2605,6 @@ void xaccAccountSetCommodity (Account * acc, gnc_commodity * com) { AccountPrivate *priv; - GList *lp; /* errors */ g_return_if_fail(GNC_IS_ACCOUNT(acc)); @@ -2626,9 +2623,8 @@ xaccAccountSetCommodity (Account * acc, gnc_commodity * com) priv->non_standard_scu = FALSE; /* iterate over splits */ - for (lp = priv->splits; lp; lp = lp->next) + for (auto s : priv->splits) { - Split *s = (Split *) lp->data; Transaction *trans = xaccSplitGetParent (s); xaccTransBeginEdit (trans); @@ -3540,35 +3536,20 @@ xaccAccountGetReconciledBalance (const Account *acc) gnc_numeric xaccAccountGetProjectedMinimumBalance (const Account *acc) { - AccountPrivate *priv; - GList *node; - time64 today; - gnc_numeric lowest = gnc_numeric_zero (); - int seen_a_transaction = 0; + auto today{gnc_time64_get_today_end()}; + std::optional minimum; - g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero()); - - priv = GET_PRIVATE(acc); - today = gnc_time64_get_today_end(); - for (node = g_list_last(priv->splits); node; node = node->prev) + auto before_today_end = [&minimum, today](const Split *s) -> bool { - Split *split = static_cast(node->data); - - if (!seen_a_transaction) - { - lowest = xaccSplitGetBalance (split); - seen_a_transaction = 1; - } - else if (gnc_numeric_compare(xaccSplitGetBalance (split), lowest) < 0) - { - lowest = xaccSplitGetBalance (split); - } - - if (xaccTransGetDate (xaccSplitGetParent (split)) <= today) - return lowest; - } - - return lowest; + auto bal{xaccSplitGetBalance(s)}; + if (!minimum || gnc_numeric_compare (bal, *minimum) < 0) + minimum = bal; + return (xaccTransGetDate(xaccSplitGetParent(s)) < today); + }; + // scan to find today's split, but we're really interested in the + // minimum balance + [[maybe_unused]] auto todays_split = gnc_account_find_split (acc, before_today_end, true); + return minimum ? *minimum : gnc_numeric_zero(); } @@ -3578,27 +3559,16 @@ xaccAccountGetProjectedMinimumBalance (const Account *acc) static gnc_numeric GetBalanceAsOfDate (Account *acc, time64 date, std::function split_to_numeric) { - /* Ideally this could use xaccAccountForEachSplit, but - * it doesn't exist yet and I'm uncertain of exactly how - * it would work at this time, since it differs from - * xaccAccountForEachTransaction by using gpointer return - * values rather than gints. - */ - Split *latest = nullptr; - g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero()); xaccAccountSortSplits (acc, TRUE); /* just in case, normally a noop */ xaccAccountRecomputeBalance (acc); /* just in case, normally a noop */ - for (GList *lp = GET_PRIVATE(acc)->splits; lp; lp = lp->next) - { - if (xaccTransGetDate (xaccSplitGetParent ((Split *)lp->data)) >= date) - break; - latest = (Split *)lp->data; - } + auto is_before_date = [date](auto s) -> bool + { return xaccTransGetDate(xaccSplitGetParent(s)) < date; }; - return latest ? split_to_numeric (latest) : gnc_numeric_zero(); + auto latest_split{gnc_account_find_split (acc, is_before_date, true)}; + return latest_split ? split_to_numeric (latest_split) : gnc_numeric_zero(); } gnc_numeric @@ -3616,19 +3586,7 @@ xaccAccountGetNoclosingBalanceAsOfDate (Account *acc, time64 date) gnc_numeric xaccAccountGetReconciledBalanceAsOfDate (Account *acc, time64 date) { - gnc_numeric balance = gnc_numeric_zero(); - - g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero()); - - for (GList *node = GET_PRIVATE(acc)->splits; node; node = node->next) - { - Split *split = (Split*) node->data; - if ((xaccSplitGetReconcile (split) == YREC) && - (xaccSplitGetDateReconciled (split) <= date)) - balance = gnc_numeric_add_fixed (balance, xaccSplitGetAmount (split)); - }; - - return balance; + return GetBalanceAsOfDate (acc, date, xaccSplitGetReconciledBalance); } /* @@ -4021,36 +3979,32 @@ xaccAccountGetNoclosingBalanceChangeInCurrencyForPeriod (Account *acc, time64 t1 /********************************************************************\ \********************************************************************/ -/* THIS API NEEDS TO CHANGE. - * - * This code exposes the internal structure of the account object to - * external callers by returning the actual list used by the object. - * It should instead return a copy of the split list that the caller - * is required to free. That change would provide the freedom of - * allowing the internal organization to change data structures if - * necessary for whatever reason, while leaving the external API - * unchanged. */ -/* XXX: violates the const'ness by forcing a sort before returning - * the splitlist */ +const SplitsVec +xaccAccountGetSplits (const Account *account) +{ + return GNC_IS_ACCOUNT(account) ? GET_PRIVATE(account)->splits : SplitsVec{}; +} + SplitList * xaccAccountGetSplitList (const Account *acc) { g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr); - xaccAccountSortSplits((Account*)acc, FALSE); // normally a noop - return GET_PRIVATE(acc)->splits; + auto priv{GET_PRIVATE(acc)}; + return std::accumulate (priv->splits.rbegin(), priv->splits.rend(), + static_cast(nullptr), g_list_prepend); } size_t xaccAccountGetSplitsSize (const Account *account) { - return GNC_IS_ACCOUNT(account) ? g_list_length (GET_PRIVATE(account)->splits) : 0; + return GNC_IS_ACCOUNT(account) ? GET_PRIVATE(account)->splits.size() : 0; } gboolean gnc_account_and_descendants_empty (Account *acc) { g_return_val_if_fail (GNC_IS_ACCOUNT (acc), FALSE); auto priv = GET_PRIVATE (acc); - if (priv->splits != nullptr) return FALSE; + if (!priv->splits.empty()) return FALSE; for (auto *n = priv->children; n; n = n->next) { if (!gnc_account_and_descendants_empty (static_cast(n->data))) @@ -5377,51 +5331,12 @@ xaccAccountGetReconcileChildrenStatus(const Account *acc) /********************************************************************\ \********************************************************************/ -/* The caller of this function can get back one or both of the - * matching split and transaction pointers, depending on whether - * a valid pointer to the location to store those pointers is - * passed. - */ -static void -finder_help_function(const Account *acc, const char *description, - Split **split, Transaction **trans ) -{ - AccountPrivate *priv; - GList *slp; - - /* First, make sure we set the data to nullptr BEFORE we start */ - if (split) *split = nullptr; - if (trans) *trans = nullptr; - - /* Then see if we have any work to do */ - if (acc == nullptr) return; - - /* Why is this loop iterated backwards ?? Presumably because the split - * list is in date order, and the most recent matches should be - * returned!? */ - priv = GET_PRIVATE(acc); - for (slp = g_list_last(priv->splits); slp; slp = slp->prev) - { - Split *lsplit = static_cast(slp->data); - Transaction *ltrans = xaccSplitGetParent(lsplit); - - if (g_strcmp0 (description, xaccTransGetDescription (ltrans)) == 0) - { - if (split) *split = lsplit; - if (trans) *trans = ltrans; - return; - } - } -} - Split * xaccAccountFindSplitByDesc(const Account *acc, const char *description) { - Split *split; - - /* Get the split which has a transaction matching the description. */ - finder_help_function(acc, description, &split, nullptr); - return split; + auto has_description = [description](const Split* s) -> bool + { return !g_strcmp0 (description, xaccTransGetDescription (xaccSplitGetParent (s))); }; + return gnc_account_find_split (acc, has_description, true); } /* This routine is for finding a matching transaction in an account by @@ -5432,11 +5347,8 @@ xaccAccountFindSplitByDesc(const Account *acc, const char *description) Transaction * xaccAccountFindTransByDesc(const Account *acc, const char *description) { - Transaction *trans; - - /* Get the translation matching the description. */ - finder_help_function(acc, description, nullptr, &trans); - return trans; + auto split = xaccAccountFindSplitByDesc (acc, description); + return split ? xaccSplitGetParent (split) : nullptr; } /* ================================================================ */ @@ -5519,8 +5431,8 @@ gnc_account_merge_children (Account *parent) gnc_account_merge_children (acc_a); /* consolidate transactions */ - while (priv_b->splits) - xaccSplitSetAccount (static_cast (priv_b->splits->data), acc_a); + while (!priv_b->splits.empty()) + xaccSplitSetAccount (priv_b->splits.front(), acc_a); /* move back one before removal. next iteration around the loop * will get the node after node_b */ @@ -5538,14 +5450,11 @@ gnc_account_merge_children (Account *parent) /* Transaction Traversal functions */ -void -xaccSplitsBeginStagedTransactionTraversals (GList *splits) +static void +xaccSplitsBeginStagedTransactionTraversals (SplitsVec splits) { - GList *lp; - - for (lp = splits; lp; lp = lp->next) + for (auto s : splits) { - Split *s = static_cast (lp->data); Transaction *trans = s->parent; if (trans) @@ -5557,12 +5466,9 @@ xaccSplitsBeginStagedTransactionTraversals (GList *splits) void xaccAccountBeginStagedTransactionTraversals (const Account *account) { - AccountPrivate *priv; - if (!account) return; - priv = GET_PRIVATE(account); - xaccSplitsBeginStagedTransactionTraversals(priv->splits); + xaccSplitsBeginStagedTransactionTraversals(GET_PRIVATE (account)->splits); } gboolean @@ -5579,16 +5485,11 @@ xaccTransactionTraverse (Transaction *trans, int stage) return FALSE; } -static void do_one_split (Split *s, gpointer data) -{ - Transaction *trans = s->parent; - trans->marker = 0; -} - static void do_one_account (Account *account, gpointer data) { AccountPrivate *priv = GET_PRIVATE(account); - g_list_foreach(priv->splits, (GFunc)do_one_split, nullptr); + std::for_each (priv->splits.begin(), priv->splits.end(), + [](auto s){ s->parent->marker = 0; }); } /* Replacement for xaccGroupBeginStagedTransactionTraversals */ @@ -5608,32 +5509,18 @@ xaccAccountStagedTransactionTraversal (const Account *acc, TransactionCallback thunk, void *cb_data) { - AccountPrivate *priv; - GList *split_p; - GList *next; - Transaction *trans; - Split *s; - int retval; - if (!acc) return 0; - priv = GET_PRIVATE(acc); - for (split_p = priv->splits; split_p; split_p = next) + auto splits = GET_PRIVATE(acc)->splits; + for (auto s : splits) { - /* Get the next element in the split list now, just in case some - * naughty thunk destroys the one we're using. This reduces, but - * does not eliminate, the possibility of undefined results if - * a thunk removes splits from this account. */ - next = g_list_next(split_p); - - s = static_cast (split_p->data); - trans = s->parent; + auto trans = s->parent; if (trans && (trans->marker < stage)) { trans->marker = stage; if (thunk) { - retval = thunk(trans, cb_data); + auto retval = thunk(trans, cb_data); if (retval) return retval; } } @@ -5649,16 +5536,14 @@ gnc_account_tree_staged_transaction_traversal (const Account *acc, void *cb_data) { const AccountPrivate *priv; - GList *acc_p, *split_p; Transaction *trans; - Split *s; int retval; if (!acc) return 0; /* depth first traversal */ priv = GET_PRIVATE(acc); - for (acc_p = priv->children; acc_p; acc_p = g_list_next(acc_p)) + for (auto acc_p = priv->children; acc_p; acc_p = g_list_next(acc_p)) { retval = gnc_account_tree_staged_transaction_traversal(static_cast (acc_p->data), stage, thunk, cb_data); @@ -5666,9 +5551,8 @@ gnc_account_tree_staged_transaction_traversal (const Account *acc, } /* Now this account */ - for (split_p = priv->splits; split_p; split_p = g_list_next(split_p)) + for (auto s : priv->splits) { - s = static_cast (split_p->data); trans = s->parent; if (trans && (trans->marker < stage)) { diff --git a/libgnucash/engine/Account.h b/libgnucash/engine/Account.h index 83fa8d3901..51379dacd6 100644 --- a/libgnucash/engine/Account.h +++ b/libgnucash/engine/Account.h @@ -1493,12 +1493,6 @@ typedef enum */ void gnc_account_tree_begin_staged_transaction_traversals(Account *acc); - /** xaccSplitsBeginStagedTransactionTraversals() resets the traversal - * marker for each transaction which is a parent of one of the - * splits in the list. - */ - void xaccSplitsBeginStagedTransactionTraversals(SplitList *splits); - /** xaccAccountBeginStagedTransactionTraversals() resets the traversal * marker for each transaction which is a parent of one of the * splits in the account. diff --git a/libgnucash/engine/Account.hpp b/libgnucash/engine/Account.hpp new file mode 100644 index 0000000000..a947bee71f --- /dev/null +++ b/libgnucash/engine/Account.hpp @@ -0,0 +1,58 @@ +/********************************************************************** + * Account.hpp + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + * * + *********************************************************************/ + +/** @addtogroup Engine + @{ */ +/** @addtogroup Account + + @{ */ +/** @file Account.hpp + * @brief Account public routines (C++ api) + */ + +#ifndef GNC_ACCOUNT_HPP +#define GNC_ACCOUNT_HPP + +#include +#include + +#include + +using SplitsVec = std::vector; + +const SplitsVec xaccAccountGetSplits (const Account*); + +/** scans account split list (in forward or reverse order) until + * predicate split->bool returns true. Maybe return the split. + * + * @param acc The account to which the split should be added. + * + * @param predicate A split->bool predicate. + * + * @param reverse To scan in reverse order + * + * @result Split* or nullptr if not found */ +Split* gnc_account_find_split (const Account*, std::function, bool); + +#endif /* GNC_COMMODITY_HPP */ +/** @} */ +/** @} */ diff --git a/libgnucash/engine/AccountP.hpp b/libgnucash/engine/AccountP.hpp index d950569a66..abd43f69c8 100644 --- a/libgnucash/engine/AccountP.hpp +++ b/libgnucash/engine/AccountP.hpp @@ -39,6 +39,7 @@ #ifndef XACC_ACCOUNT_P_H #define XACC_ACCOUNT_P_H +#include #include #include "Account.h" @@ -118,7 +119,7 @@ typedef struct AccountPrivate gboolean balance_dirty; /* balances in splits incorrect */ - GList *splits; /* list of split pointers */ + std::vector splits; /* list of split pointers */ gboolean sort_dirty; /* sort order of splits is bad */ LotList *lots; /* list of lot pointers */ diff --git a/libgnucash/engine/CMakeLists.txt b/libgnucash/engine/CMakeLists.txt index 84178c99ca..dbd2516c9e 100644 --- a/libgnucash/engine/CMakeLists.txt +++ b/libgnucash/engine/CMakeLists.txt @@ -31,6 +31,7 @@ set(engine_noinst_HEADERS ) set (engine_HEADERS + Account.hpp Account.h FreqSpec.h Recurrence.h diff --git a/libgnucash/engine/SX-book.c b/libgnucash/engine/SX-book.c index b784bab150..1aff588358 100644 --- a/libgnucash/engine/SX-book.c +++ b/libgnucash/engine/SX-book.c @@ -385,6 +385,7 @@ gnc_sx_get_sxes_referencing_account(QofBook *book, Account *acct) guid_free (guid); } + g_list_free (splits); } return g_list_reverse (rtn); } diff --git a/libgnucash/engine/SchedXaction.c b/libgnucash/engine/SchedXaction.c index 5ad3480b9b..266bbd99f3 100644 --- a/libgnucash/engine/SchedXaction.c +++ b/libgnucash/engine/SchedXaction.c @@ -454,6 +454,7 @@ delete_template_trans(SchedXaction *sx) sxprivTransMapDelete, NULL); + g_list_free (templ_acct_splits); g_list_free (templ_acct_transactions); return; } diff --git a/libgnucash/engine/SchedXaction.h b/libgnucash/engine/SchedXaction.h index e58b6d2042..9e1f65719a 100644 --- a/libgnucash/engine/SchedXaction.h +++ b/libgnucash/engine/SchedXaction.h @@ -217,6 +217,7 @@ gint gnc_sx_get_instance_count( const SchedXaction *sx, /*@ null @*/ SXTmpStateD */ void gnc_sx_set_instance_count( SchedXaction *sx, gint instanceNum ); +/* must be g_list_freed */ GList *xaccSchedXactionGetSplits( const SchedXaction *sx ); void xaccSchedXactionSetSplits( SchedXaction *sx, GList *newSplits ); diff --git a/libgnucash/engine/Scrub.cpp b/libgnucash/engine/Scrub.cpp index 0530325dfc..f468f13f76 100644 --- a/libgnucash/engine/Scrub.cpp +++ b/libgnucash/engine/Scrub.cpp @@ -47,6 +47,7 @@ #include "Account.h" #include "AccountP.hpp" +#include "Account.hpp" #include "Scrub.h" #include "Transaction.h" #include "TransactionP.h" @@ -91,8 +92,8 @@ gnc_get_ongoing_scrub (void) static void add_transactions (const Account *account, GHashTable **ht) { - for (GList *m = xaccAccountGetSplitList (account); m; m = g_list_next (m)) - g_hash_table_add (*ht, xaccSplitGetParent (GNC_SPLIT(m->data))); + for (auto s : xaccAccountGetSplits (account)) + g_hash_table_add (*ht, xaccSplitGetParent (s)); } static GList* @@ -226,12 +227,11 @@ xaccAccountTreeScrubSplits (Account *account) void xaccAccountScrubSplits (Account *account) { - GList *node; scrub_depth++; - for (node = xaccAccountGetSplitList (account); node; node = node->next) + for (auto s : xaccAccountGetSplits (account)) { if (abort_now) break; - xaccSplitScrub (GNC_SPLIT(node->data)); + xaccSplitScrub (s); } scrub_depth--; } diff --git a/libgnucash/engine/Scrub2.cpp b/libgnucash/engine/Scrub2.cpp index 6743895177..265492f738 100644 --- a/libgnucash/engine/Scrub2.cpp +++ b/libgnucash/engine/Scrub2.cpp @@ -37,6 +37,7 @@ #include "qof.h" #include "Account.h" #include "AccountP.hpp" +#include "Account.hpp" #include "Transaction.h" #include "TransactionP.h" #include "Scrub2.h" @@ -57,19 +58,14 @@ static QofLogModule log_module = GNC_MOD_LOT; void xaccAccountAssignLots (Account *acc) { - SplitList *splits, *node; - if (!acc) return; ENTER ("acc=%s", xaccAccountGetName(acc)); xaccAccountBeginEdit (acc); restart_loop: - splits = xaccAccountGetSplitList(acc); - for (node = splits; node; node = node->next) + for (auto split : xaccAccountGetSplits (acc)) { - Split * split = GNC_SPLIT(node->data); - /* If already in lot, then no-op */ if (split->lot) continue; diff --git a/libgnucash/engine/ScrubBusiness.c b/libgnucash/engine/ScrubBusiness.c index 0a68fc9531..f2b765beed 100644 --- a/libgnucash/engine/ScrubBusiness.c +++ b/libgnucash/engine/ScrubBusiness.c @@ -410,6 +410,7 @@ gncScrubLotDanglingPayments (GNCLot *lot) filtered_list = g_list_prepend (filtered_list, free_split); } + g_list_free (split_list); filtered_list = g_list_reverse (filtered_list); match_list = gncSLFindOffsSplits (filtered_list, ll_val); @@ -695,7 +696,7 @@ gncScrubBusinessAccountSplits (Account *acc, QofPercentageFunc percentagefunc) restart: curr_split_no = 0; splits = xaccAccountGetSplitList(acc); - split_count = g_list_length (splits); + split_count = xaccAccountGetSplitsSize (acc); for (node = splits; node; node = node->next) { Split *split = node->data; @@ -723,6 +724,7 @@ restart: curr_split_no + 1, split_count); curr_split_no++; } + g_list_free (splits); xaccAccountCommitEdit(acc); (percentagefunc)(NULL, -1.0); LEAVE ("(acc=%s)", str); diff --git a/libgnucash/engine/cap-gains.cpp b/libgnucash/engine/cap-gains.cpp index 0754005fc7..3c004c552b 100644 --- a/libgnucash/engine/cap-gains.cpp +++ b/libgnucash/engine/cap-gains.cpp @@ -58,6 +58,7 @@ ToDo: #include #include +#include "Account.hpp" #include "AccountP.hpp" #include "Scrub2.h" #include "Scrub3.h" @@ -79,7 +80,6 @@ gboolean xaccAccountHasTrades (const Account *acc) { gnc_commodity *acc_comm; - SplitList *splits, *node; if (!acc) return FALSE; @@ -88,10 +88,8 @@ xaccAccountHasTrades (const Account *acc) acc_comm = xaccAccountGetCommodity(acc); - splits = xaccAccountGetSplitList(acc); - for (node = splits; node; node = node->next) + for (auto s : xaccAccountGetSplits (acc)) { - Split *s = GNC_SPLIT(node->data); Transaction *t = s->parent; if (s->gains == GAINS_STATUS_GAINS) continue; if (acc_comm != t->common_currency) return TRUE; diff --git a/libgnucash/engine/mocks/gmock-Account.cpp b/libgnucash/engine/mocks/gmock-Account.cpp index b85ce2527a..3250a2c1b4 100644 --- a/libgnucash/engine/mocks/gmock-Account.cpp +++ b/libgnucash/engine/mocks/gmock-Account.cpp @@ -76,6 +76,16 @@ xaccAccountGetSplitList (const Account *account) return mockaccount ? mockaccount->xaccAccountGetSplitList() : nullptr; } +const std::vector +xaccAccountGetSplits (const Account *account) +{ + SCOPED_TRACE(""); + auto mockaccount = gnc_mockaccount(account); + if (!mockaccount) + return {}; + return mockaccount->xaccAccountGetSplits(); +} + Account* gnc_account_imap_find_account ( diff --git a/libgnucash/engine/mocks/gmock-Account.h b/libgnucash/engine/mocks/gmock-Account.h index fc71b0f8f7..97860fdc5e 100644 --- a/libgnucash/engine/mocks/gmock-Account.h +++ b/libgnucash/engine/mocks/gmock-Account.h @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -45,6 +46,7 @@ public: MOCK_CONST_METHOD0(get_commodity, gnc_commodity*()); MOCK_CONST_METHOD2(for_each_transaction, gint(TransactionCallback, void*)); MOCK_CONST_METHOD0(xaccAccountGetSplitList, SplitList*()); + MOCK_CONST_METHOD0(xaccAccountGetSplits, std::vector()); MOCK_METHOD2(find_account, Account *(const char*, const char*)); MOCK_METHOD3(add_account, void(const char*, const char*, Account*)); MOCK_METHOD1(find_account_bayes, Account *(std::vector&)); diff --git a/libgnucash/engine/policy.cpp b/libgnucash/engine/policy.cpp index ce89264219..35febca6f4 100644 --- a/libgnucash/engine/policy.cpp +++ b/libgnucash/engine/policy.cpp @@ -34,6 +34,7 @@ #include #include "Account.h" +#include "Account.hpp" #include "Transaction.h" #include "TransactionP.h" #include "cap-gains.h" @@ -100,38 +101,16 @@ DirectionPolicyGetSplit (GNCPolicy *pcy, GNCLot *lot, short reverse) * hasn't been assigned to a lot. Return that split. * Make use of the fact that the splits in an account are * already in date order; so we don't have to sort. */ - auto splits = xaccAccountGetSplitList (lot_account); - - Split *rv = nullptr; - - for (auto node = reverse ? g_list_last (splits) : splits; !rv && node; - node = reverse ? node->prev : node->next) + auto find_split = [open_time, common_currency, want_positive](const Split* split) { - split = GNC_SPLIT(node->data); - if (split->lot) - continue; + return (!split->lot && + xaccTransRetDatePosted (xaccSplitGetParent (split)) >= open_time && + gnc_commodity_equiv (common_currency, split->parent->common_currency) && + !gnc_numeric_zero_p (split->amount) && + want_positive == gnc_numeric_positive_p (split->amount)); + }; - /* Skip it if it's too early */ - if (xaccTransRetDatePosted (xaccSplitGetParent (split)) < open_time) - { - if (reverse) - /* Going backwards, no point in looking further */ - break; - continue; - } - - /* Allow equiv currencies */ - if (!gnc_commodity_equiv (common_currency, split->parent->common_currency)) - continue; - - /* Disallow zero-amount splits in general. */ - if (gnc_numeric_zero_p(split->amount)) - continue; - - if (want_positive == gnc_numeric_positive_p (split->amount)) - rv = split; - } - return rv; + return gnc_account_find_split (lot_account, find_split, reverse); } /* ============================================================== */ diff --git a/libgnucash/engine/test-core/test-engine-stuff.cpp b/libgnucash/engine/test-core/test-engine-stuff.cpp index 3eb0479e36..2024f8fb9a 100644 --- a/libgnucash/engine/test-core/test-engine-stuff.cpp +++ b/libgnucash/engine/test-core/test-engine-stuff.cpp @@ -1074,7 +1074,6 @@ make_random_changes_to_level (QofBook *book, Account *parent) account = static_cast(get_random_list_element (accounts)); splits = xaccAccountGetSplitList (account); - splits = g_list_copy (splits); for (node = splits; node; node = node->next) { diff --git a/libgnucash/engine/test/utest-Account.cpp b/libgnucash/engine/test/utest-Account.cpp index 7c52744959..6a64a362c4 100644 --- a/libgnucash/engine/test/utest-Account.cpp +++ b/libgnucash/engine/test/utest-Account.cpp @@ -882,7 +882,7 @@ test_xaccFreeAccount (Fixture *fixture, gconstpointer pData) /* Check that we've got children, lots, and splits to remove */ g_assert_true (p_priv->children != NULL); g_assert_true (p_priv->lots != NULL); - g_assert_true (p_priv->splits != NULL); + g_assert_true (p_priv->splits.size()); g_assert_true (p_priv->parent != NULL); g_assert_true (p_priv->commodity != NULL); g_assert_cmpint (check1->hits, ==, 0); @@ -983,7 +983,7 @@ test_xaccAccountCommitEdit (Fixture *fixture, gconstpointer pData) /* Check that we've got children, lots, and splits to remove */ g_assert_true (p_priv->children != NULL); g_assert_true (p_priv->lots != NULL); - g_assert_true (p_priv->splits != NULL); + g_assert_true (p_priv->splits.size()); g_assert_true (p_priv->parent != NULL); g_assert_true (p_priv->commodity != NULL); g_assert_cmpint (check1->hits, ==, 0); @@ -998,7 +998,7 @@ test_xaccAccountCommitEdit (Fixture *fixture, gconstpointer pData) test_signal_assert_hits (sig2, 0); g_assert_true (p_priv->children != NULL); g_assert_true (p_priv->lots != NULL); - g_assert_true (p_priv->splits != NULL); + g_assert_true (p_priv->splits.size()); g_assert_true (p_priv->parent != NULL); g_assert_true (p_priv->commodity != NULL); g_assert_cmpint (check1->hits, ==, 0); @@ -1393,19 +1393,19 @@ test_gnc_account_insert_remove_split (Fixture *fixture, gconstpointer pData) /* Check that the call fails with invalid account and split (throws) */ g_assert_true (!gnc_account_insert_split (NULL, split1)); - g_assert_cmpuint (g_list_length (priv->splits), == , 0); + g_assert_cmpuint (priv->splits.size(), == , 0); g_assert_true (!priv->sort_dirty); g_assert_true (!priv->balance_dirty); test_signal_assert_hits (sig1, 0); test_signal_assert_hits (sig2, 0); g_assert_true (!gnc_account_insert_split (fixture->acct, NULL)); - g_assert_cmpuint (g_list_length (priv->splits), == , 0); + g_assert_cmpuint (priv->splits.size(), == , 0); g_assert_true (!priv->sort_dirty); g_assert_true (!priv->balance_dirty); test_signal_assert_hits (sig1, 0); test_signal_assert_hits (sig2, 0); /* g_assert_true (!gnc_account_insert_split (fixture->acct, (Split*)priv)); */ - /* g_assert_cmpuint (g_list_length (priv->splits), == , 0); */ + /* g_assert_cmpuint (priv->splits.size(), == , 0); */ /* g_assert_true (!priv->sort_dirty); */ /* g_assert_true (!priv->balance_dirty); */ /* test_signal_assert_hits (sig1, 0); */ @@ -1418,7 +1418,7 @@ test_gnc_account_insert_remove_split (Fixture *fixture, gconstpointer pData) /* Check that it works the first time */ g_assert_true (gnc_account_insert_split (fixture->acct, split1)); - g_assert_cmpuint (g_list_length (priv->splits), == , 1); + g_assert_cmpuint (priv->splits.size(), == , 1); g_assert_true (!priv->sort_dirty); g_assert_true (priv->balance_dirty); test_signal_assert_hits (sig1, 1); @@ -1430,7 +1430,7 @@ test_gnc_account_insert_remove_split (Fixture *fixture, gconstpointer pData) sig3 = test_signal_new (&fixture->acct->inst, GNC_EVENT_ITEM_ADDED, split2); /* Now add a second split to the account and check that sort_dirty isn't set. We have to bump the editlevel to force this. */ g_assert_true (gnc_account_insert_split (fixture->acct, split2)); - g_assert_cmpuint (g_list_length (priv->splits), == , 2); + g_assert_cmpuint (priv->splits.size(), == , 2); g_assert_true (!priv->sort_dirty); g_assert_true (priv->balance_dirty); test_signal_assert_hits (sig1, 2); @@ -1441,7 +1441,7 @@ test_gnc_account_insert_remove_split (Fixture *fixture, gconstpointer pData) qof_instance_increase_editlevel (fixture->acct); g_assert_true (gnc_account_insert_split (fixture->acct, split3)); qof_instance_decrease_editlevel (fixture->acct); - g_assert_cmpuint (g_list_length (priv->splits), == , 3); + g_assert_cmpuint (priv->splits.size(), == , 3); g_assert_true (priv->sort_dirty); g_assert_true (priv->balance_dirty); test_signal_assert_hits (sig1, 3); @@ -1452,7 +1452,7 @@ test_gnc_account_insert_remove_split (Fixture *fixture, gconstpointer pData) sig3 = test_signal_new (&fixture->acct->inst, GNC_EVENT_ITEM_REMOVED, split3); g_assert_true (gnc_account_remove_split (fixture->acct, split3)); - g_assert_cmpuint (g_list_length (priv->splits), == , 2); + g_assert_cmpuint (priv->splits.size(), == , 2); g_assert_true (priv->sort_dirty); g_assert_true (!priv->balance_dirty); test_signal_assert_hits (sig1, 4); @@ -1460,7 +1460,7 @@ test_gnc_account_insert_remove_split (Fixture *fixture, gconstpointer pData) /* And do it again to make sure that it fails when the split has * already been removed */ g_assert_true (!gnc_account_remove_split (fixture->acct, split3)); - g_assert_cmpuint (g_list_length (priv->splits), == , 2); + g_assert_cmpuint (priv->splits.size(), == , 2); g_assert_true (priv->sort_dirty); g_assert_true (!priv->balance_dirty); test_signal_assert_hits (sig1, 4); diff --git a/libgnucash/engine/test/utest-Invoice.c b/libgnucash/engine/test/utest-Invoice.c index a069c56f89..fbb4b7db78 100644 --- a/libgnucash/engine/test/utest-Invoice.c +++ b/libgnucash/engine/test/utest-Invoice.c @@ -329,8 +329,7 @@ test_invoice_doclink ( Fixture *fixture, gconstpointer pData ) static gboolean account_has_one_split (const Account *acc) { - GList *splits = xaccAccountGetSplitList (acc); - return (splits && !(splits->next)); + return (xaccAccountGetSplitsSize (acc) == 1); } static void