LCOV - code coverage report
Current view: top level - libgnucash/engine - Account.cpp (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 77.2 % 2448 1891
Test Date: 2026-11-02 18:19:43 Functions: 80.8 % 317 256
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /********************************************************************\
       2              :  * Account.c -- Account data structure implementation               *
       3              :  * Copyright (C) 1997 Robin D. Clark                                *
       4              :  * Copyright (C) 1997-2003 Linas Vepstas <linas@linas.org>          *
       5              :  * Copyright (C) 2007 David Hampton <hampton@employees.org>         *
       6              :  *                                                                  *
       7              :  * This program is free software; you can redistribute it and/or    *
       8              :  * modify it under the terms of the GNU General Public License as   *
       9              :  * published by the Free Software Foundation; either version 2 of   *
      10              :  * the License, or (at your option) any later version.              *
      11              :  *                                                                  *
      12              :  * This program is distributed in the hope that it will be useful,  *
      13              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
      14              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
      15              :  * GNU General Public License for more details.                     *
      16              :  *                                                                  *
      17              :  * You should have received a copy of the GNU General Public License*
      18              :  * along with this program; if not, contact:                        *
      19              :  *                                                                  *
      20              :  * Free Software Foundation           Voice:  +1-617-542-5942       *
      21              :  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
      22              :  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
      23              :  *                                                                  *
      24              : \********************************************************************/
      25              : 
      26              : #include <config.h>
      27              : 
      28              : #include "gnc-prefs.h"
      29              : 
      30              : #include <glib.h>
      31              : #include <glib/gi18n.h>
      32              : #include <stdlib.h>
      33              : #include <stdint.h>
      34              : #include <string.h>
      35              : 
      36              : #include "AccountP.hpp"
      37              : #include "Account.hpp"
      38              : #include "Split.h"
      39              : #include "Transaction.h"
      40              : #include "TransactionP.hpp"
      41              : #include "gnc-event.h"
      42              : #include "gnc-glib-utils.h"
      43              : #include "gnc-lot.h"
      44              : #include "gnc-pricedb.h"
      45              : #include "qofevent.h"
      46              : #include "qofinstance-p.h"
      47              : #include "gnc-features.h"
      48              : #include "guid.hpp"
      49              : 
      50              : #include <numeric>
      51              : #include <map>
      52              : #include <unordered_set>
      53              : #include <algorithm>
      54              : 
      55              : static QofLogModule log_module = GNC_MOD_ACCOUNT;
      56              : 
      57              : /* The Canonical Account Separator.  Pre-Initialized. */
      58              : static gchar account_separator[8] = ".";
      59              : static gunichar account_uc_separator = ':';
      60              : 
      61              : static bool imap_convert_bayes_to_flat_run = false;
      62              : 
      63              : /* Predefined KVP paths */
      64              : static const std::string KEY_ASSOC_INCOME_ACCOUNT("ofx/associated-income-account");
      65              : static const std::string KEY_RECONCILE_INFO("reconcile-info");
      66              : static const std::string KEY_INCLUDE_CHILDREN("include-children");
      67              : static const std::string KEY_POSTPONE("postpone");
      68              : static const std::string KEY_LOT_MGMT("lot-mgmt");
      69              : static const std::string KEY_ONLINE_ID("online_id");
      70              : static const std::string KEY_IMP_APPEND_TEXT("import-append-text");
      71              : static const std::string AB_KEY("hbci");
      72              : static const std::string AB_ACCOUNT_ID("account-id");
      73              : static const std::string AB_ACCOUNT_UID("account-uid");
      74              : static const std::string AB_BANK_CODE("bank-code");
      75              : static const std::string AB_TRANS_RETRIEVAL("trans-retrieval");
      76              : 
      77              : static const std::string KEY_BALANCE_LIMIT("balance-limit");
      78              : static const std::string KEY_BALANCE_HIGHER_LIMIT_VALUE("higher-value");
      79              : static const std::string KEY_BALANCE_LOWER_LIMIT_VALUE("lower-value");
      80              : static const std::string KEY_BALANCE_INCLUDE_SUB_ACCTS("inlude-sub-accts");
      81              : 
      82              : using FinalProbabilityVec=std::vector<std::pair<std::string, int32_t>>;
      83              : using ProbabilityVec=std::vector<std::pair<std::string, struct AccountProbability>>;
      84              : using FlatKvpEntry=std::pair<std::string, KvpValue*>;
      85              : 
      86              : enum
      87              : {
      88              :     LAST_SIGNAL
      89              : };
      90              : 
      91              : enum
      92              : {
      93              :     PROP_0,
      94              :     PROP_NAME,                          /* Table */
      95              :     PROP_FULL_NAME,                     /* Constructed */
      96              :     PROP_CODE,                          /* Table */
      97              :     PROP_DESCRIPTION,                   /* Table */
      98              :     PROP_COLOR,                         /* KVP */
      99              :     PROP_NOTES,                         /* KVP */
     100              :     PROP_TYPE,                          /* Table */
     101              : 
     102              : //    PROP_PARENT,                      /* Table, Not a property */
     103              :     PROP_COMMODITY,                     /* Table */
     104              :     PROP_COMMODITY_SCU,                 /* Table */
     105              :     PROP_NON_STD_SCU,                   /* Table */
     106              :     PROP_END_BALANCE,                   /* Constructed */
     107              :     PROP_END_NOCLOSING_BALANCE,         /* Constructed */
     108              :     PROP_END_CLEARED_BALANCE,           /* Constructed */
     109              :     PROP_END_RECONCILED_BALANCE,        /* Constructed */
     110              : 
     111              :     PROP_TAX_RELATED,                   /* KVP */
     112              :     PROP_TAX_CODE,                      /* KVP */
     113              :     PROP_TAX_SOURCE,                    /* KVP */
     114              :     PROP_TAX_COPY_NUMBER,               /* KVP */
     115              : 
     116              :     PROP_HIDDEN,                        /* Table slot exists, but in KVP in memory & xml */
     117              :     PROP_PLACEHOLDER,                   /* Table slot exists, but in KVP in memory & xml */
     118              :     PROP_AUTO_INTEREST,
     119              :     PROP_FILTER,                        /* KVP */
     120              :     PROP_SORT_ORDER,                    /* KVP */
     121              :     PROP_SORT_REVERSED,
     122              : 
     123              :     PROP_LOT_NEXT_ID,                   /* KVP */
     124              :     PROP_ONLINE_ACCOUNT,                /* KVP */
     125              :     PROP_IMP_APPEND_TEXT,               /* KVP */
     126              :     PROP_IS_OPENING_BALANCE,            /* KVP */
     127              :     PROP_OFX_INCOME_ACCOUNT,            /* KVP */
     128              :     PROP_AB_ACCOUNT_ID,                 /* KVP */
     129              :     PROP_AB_ACCOUNT_UID,                /* KVP */
     130              :     PROP_AB_BANK_CODE,                  /* KVP */
     131              :     PROP_AB_TRANS_RETRIEVAL,            /* KVP */
     132              : 
     133              :     PROP_RUNTIME_0,
     134              :     PROP_POLICY,                        /* Cached Value */
     135              :     PROP_MARK,                          /* Runtime Value */
     136              :     PROP_SORT_DIRTY,                    /* Runtime Value */
     137              :     PROP_BALANCE_DIRTY,                 /* Runtime Value */
     138              :     PROP_START_BALANCE,                 /* Runtime Value */
     139              :     PROP_START_NOCLOSING_BALANCE,       /* Runtime Value */
     140              :     PROP_START_CLEARED_BALANCE,         /* Runtime Value */
     141              :     PROP_START_RECONCILED_BALANCE,      /* Runtime Value */
     142              : };
     143              : 
     144              : #define GET_PRIVATE(o)  \
     145              :     ((AccountPrivate*)gnc_account_get_instance_private((Account*)o))
     146              : 
     147              : /* This map contains a set of strings representing the different column types. */
     148              : static const std::map<GNCAccountType, const char*> gnc_acct_debit_strs = {
     149              :     { ACCT_TYPE_NONE,       N_("Funds In") },
     150              :     { ACCT_TYPE_BANK,       N_("Deposit") },
     151              :     { ACCT_TYPE_CASH,       N_("Receive") },
     152              :     { ACCT_TYPE_CREDIT,     N_("Payment") },
     153              :     { ACCT_TYPE_ASSET,      N_("Increase") },
     154              :     { ACCT_TYPE_LIABILITY,  N_("Decrease") },
     155              :     { ACCT_TYPE_STOCK,      N_("Buy") },
     156              :     { ACCT_TYPE_MUTUAL,     N_("Buy") },
     157              :     { ACCT_TYPE_CURRENCY,   N_("Buy") },
     158              :     { ACCT_TYPE_INCOME,     N_("Charge") },
     159              :     { ACCT_TYPE_EXPENSE,    N_("Expense") },
     160              :     { ACCT_TYPE_PAYABLE,    N_("Payment") },
     161              :     { ACCT_TYPE_RECEIVABLE, N_("Invoice") },
     162              :     { ACCT_TYPE_TRADING,    N_("Decrease") },
     163              :     { ACCT_TYPE_EQUITY,     N_("Decrease") },
     164              : };
     165              : static const char* dflt_acct_debit_str = N_("Debit");
     166              : 
     167              : /* This map contains a set of strings representing the different column types. */
     168              : static const std::map<GNCAccountType, const char*> gnc_acct_credit_strs = {
     169              :     { ACCT_TYPE_NONE,       N_("Funds Out") },
     170              :     { ACCT_TYPE_BANK,       N_("Withdrawal") },
     171              :     { ACCT_TYPE_CASH,       N_("Spend") },
     172              :     { ACCT_TYPE_CREDIT,     N_("Charge") },
     173              :     { ACCT_TYPE_ASSET,      N_("Decrease") },
     174              :     { ACCT_TYPE_LIABILITY,  N_("Increase") },
     175              :     { ACCT_TYPE_STOCK,      N_("Sell") },
     176              :     { ACCT_TYPE_MUTUAL,     N_("Sell") },
     177              :     { ACCT_TYPE_CURRENCY,   N_("Sell") },
     178              :     { ACCT_TYPE_INCOME,     N_("Income") },
     179              :     { ACCT_TYPE_EXPENSE,    N_("Rebate") },
     180              :     { ACCT_TYPE_PAYABLE,    N_("Bill") },
     181              :     { ACCT_TYPE_RECEIVABLE, N_("Payment") },
     182              :     { ACCT_TYPE_TRADING,    N_("Increase") },
     183              :     { ACCT_TYPE_EQUITY,     N_("Increase") },
     184              : };
     185              : static const char* dflt_acct_credit_str = N_("Credit");
     186              : 
     187              : /********************************************************************\
     188              :  * Because I can't use C++ for this project, doesn't mean that I    *
     189              :  * can't pretend to!  These functions perform actions on the        *
     190              :  * account data structure, in order to encapsulate the knowledge    *
     191              :  * of the internals of the Account in one file.                     *
     192              : \********************************************************************/
     193              : 
     194              : static void xaccAccountBringUpToDate (Account *acc);
     195              : 
     196              : 
     197              : /********************************************************************\
     198              :  * gnc_get_account_separator                                        *
     199              :  *   returns the current account separator character                *
     200              :  *                                                                  *
     201              :  * Args: none                                                       *
     202              :  * Returns: account separator character                             *
     203              :  \*******************************************************************/
     204              : const gchar *
     205           23 : gnc_get_account_separator_string (void)
     206              : {
     207           23 :     return account_separator;
     208              : }
     209              : 
     210              : gunichar
     211            3 : gnc_get_account_separator (void)
     212              : {
     213            3 :     return account_uc_separator;
     214              : }
     215              : 
     216              : void
     217           36 : gnc_set_account_separator (const gchar *separator)
     218              : {
     219              :     gunichar uc;
     220              :     gint count;
     221              : 
     222           36 :     uc = g_utf8_get_char_validated(separator, -1);
     223           36 :     if ((uc == (gunichar) - 2) || (uc == (gunichar) - 1) || g_unichar_isalnum(uc))
     224              :     {
     225            1 :         account_uc_separator = ':';
     226            1 :         strcpy(account_separator, ":");
     227            1 :         return;
     228              :     }
     229              : 
     230           35 :     account_uc_separator = uc;
     231           35 :     count = g_unichar_to_utf8(uc, account_separator);
     232           35 :     account_separator[count] = '\0';
     233              : }
     234              : 
     235            3 : gchar *gnc_account_name_violations_errmsg (const gchar *separator, GList* invalid_account_names)
     236              : {
     237            3 :     gchar *message = nullptr;
     238              : 
     239            3 :     if ( !invalid_account_names )
     240            2 :         return nullptr;
     241              : 
     242            1 :     auto account_list {gnc_g_list_stringjoin (invalid_account_names, "\n")};
     243              : 
     244              :     /* Translators: The first %s will be the account separator character,
     245              :        the second %s is a list of account names.
     246              :        The resulting string will be displayed to the user if there are
     247              :        account names containing the separator character. */
     248            1 :     message = g_strdup_printf(
     249            1 :                   _("The separator character \"%s\" is used in one or more account names.\n\n"
     250              :                     "This will result in unexpected behaviour. "
     251              :                     "Either change the account names or choose another separator character.\n\n"
     252              :                     "Below you will find the list of invalid account names:\n"
     253              :                     "%s"), separator, account_list );
     254            1 :     g_free ( account_list );
     255            1 :     return message;
     256              : }
     257              : 
     258              : struct ViolationData
     259              : {
     260              :     GList *list;
     261              :     const gchar *separator;
     262              : };
     263              : 
     264              : static void
     265            3 : check_acct_name (Account *acct, gpointer user_data)
     266              : {
     267            3 :     auto cb {static_cast<ViolationData*>(user_data)};
     268            3 :     auto name {xaccAccountGetName (acct)};
     269            3 :     if (g_strstr_len (name, -1, cb->separator))
     270            2 :         cb->list = g_list_prepend (cb->list, g_strdup (name));
     271            3 : }
     272              : 
     273            4 : GList *gnc_account_list_name_violations (QofBook *book, const gchar *separator)
     274              : {
     275            4 :     g_return_val_if_fail (separator != nullptr, nullptr);
     276            2 :     if (!book) return nullptr;
     277            1 :     ViolationData cb = { nullptr, separator };
     278            1 :     gnc_account_foreach_descendant (gnc_book_get_root_account (book),
     279              :                                     (AccountCb)check_acct_name, &cb);
     280            1 :     return cb.list;
     281              : }
     282              : 
     283              : /********************************************************************\
     284              : \********************************************************************/
     285              : 
     286              : static inline void mark_account (Account *acc);
     287              : void
     288        16767 : mark_account (Account *acc)
     289              : {
     290        16767 :     qof_instance_set_dirty(&acc->inst);
     291        16767 : }
     292              : 
     293              : /********************************************************************\
     294              : \********************************************************************/
     295              : 
     296              : /* GObject Initialization */
     297      1215262 : G_DEFINE_TYPE_WITH_PRIVATE(Account, gnc_account, QOF_TYPE_INSTANCE)
     298              : 
     299              : static void
     300         5073 : gnc_account_init(Account* acc)
     301              : {
     302              :     AccountPrivate *priv;
     303              : 
     304         5073 :     priv = GET_PRIVATE(acc);
     305         5073 :     priv->parent   = nullptr;
     306              : 
     307         5073 :     priv->accountName = qof_string_cache_insert("");
     308         5073 :     priv->accountCode = qof_string_cache_insert("");
     309         5073 :     priv->description = qof_string_cache_insert("");
     310              : 
     311         5073 :     priv->type = ACCT_TYPE_NONE;
     312              : 
     313         5073 :     priv->mark = 0;
     314              : 
     315         5073 :     priv->policy = xaccGetFIFOPolicy();
     316         5073 :     priv->lots = nullptr;
     317              : 
     318         5073 :     priv->commodity = nullptr;
     319         5073 :     priv->commodity_scu = 0;
     320         5073 :     priv->non_standard_scu = FALSE;
     321              : 
     322         5073 :     priv->balance = gnc_numeric_zero();
     323         5073 :     priv->noclosing_balance = gnc_numeric_zero();
     324         5073 :     priv->cleared_balance = gnc_numeric_zero();
     325         5073 :     priv->reconciled_balance = gnc_numeric_zero();
     326         5073 :     priv->starting_balance = gnc_numeric_zero();
     327         5073 :     priv->starting_noclosing_balance = gnc_numeric_zero();
     328         5073 :     priv->starting_cleared_balance = gnc_numeric_zero();
     329         5073 :     priv->starting_reconciled_balance = gnc_numeric_zero();
     330         5073 :     priv->balance_dirty = FALSE;
     331         5073 :     priv->has_stock_split = false;
     332              : 
     333         5073 :     new (&priv->children) AccountVec ();
     334         5073 :     new (&priv->splits) SplitsVec ();
     335         5073 :     priv->splits_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
     336         5073 :     priv->sort_dirty = FALSE;
     337         5073 : }
     338              : 
     339              : static void
     340         2385 : gnc_account_dispose (GObject *acctp)
     341              : {
     342         2385 :     G_OBJECT_CLASS(gnc_account_parent_class)->dispose(acctp);
     343         2385 : }
     344              : 
     345              : static void
     346         2385 : gnc_account_finalize(GObject* acctp)
     347              : {
     348         2385 :     G_OBJECT_CLASS(gnc_account_parent_class)->finalize(acctp);
     349         2385 : }
     350              : 
     351              : /* Note that g_value_set_object() refs the object, as does
     352              :  * g_object_get(). But g_object_get() only unrefs once when it disgorges
     353              :  * the object, leaving an unbalanced ref, which leaks. So instead of
     354              :  * using g_value_set_object(), use g_value_take_object() which doesn't
     355              :  * ref the object when used in get_property().
     356              :  */
     357              : static void
     358          597 : gnc_account_get_property (GObject         *object,
     359              :                           guint            prop_id,
     360              :                           GValue          *value,
     361              :                           GParamSpec      *pspec)
     362              : {
     363              :     Account *account;
     364              :     AccountPrivate *priv;
     365              : 
     366          597 :     g_return_if_fail(GNC_IS_ACCOUNT(object));
     367              : 
     368          597 :     account = GNC_ACCOUNT(object);
     369          597 :     priv = GET_PRIVATE(account);
     370          597 :     switch (prop_id)
     371              :     {
     372           96 :     case PROP_NAME:
     373           96 :         g_value_set_string(value, priv->accountName);
     374           96 :         break;
     375            1 :     case PROP_FULL_NAME:
     376            1 :         g_value_take_string(value, gnc_account_get_full_name(account));
     377            1 :         break;
     378           83 :     case PROP_CODE:
     379           83 :         g_value_set_string(value, priv->accountCode);
     380           83 :         break;
     381           75 :     case PROP_DESCRIPTION:
     382           75 :         g_value_set_string(value, priv->description);
     383           75 :         break;
     384            1 :     case PROP_COLOR:
     385            1 :         g_value_set_string(value, xaccAccountGetColor(account));
     386            1 :         break;
     387            1 :     case PROP_NOTES:
     388            1 :         g_value_set_string(value, xaccAccountGetNotes(account));
     389            1 :         break;
     390           35 :     case PROP_TYPE:
     391              :         // NEED TO BE CONVERTED TO A G_TYPE_ENUM
     392           35 :         g_value_set_int(value, priv->type);
     393           35 :         break;
     394           41 :     case PROP_COMMODITY:
     395           41 :         g_value_take_object(value, priv->commodity);
     396           41 :         break;
     397           41 :     case PROP_COMMODITY_SCU:
     398           41 :         g_value_set_int(value, priv->commodity_scu);
     399           41 :         break;
     400           41 :     case PROP_NON_STD_SCU:
     401           41 :         g_value_set_boolean(value, priv->non_standard_scu);
     402           41 :         break;
     403            9 :     case PROP_SORT_DIRTY:
     404            9 :         g_value_set_boolean(value, priv->sort_dirty);
     405            9 :         break;
     406            9 :     case PROP_BALANCE_DIRTY:
     407            9 :         g_value_set_boolean(value, priv->balance_dirty);
     408            9 :         break;
     409            3 :     case PROP_START_BALANCE:
     410            3 :         g_value_set_boxed(value, &priv->starting_balance);
     411            3 :         break;
     412            0 :     case PROP_START_NOCLOSING_BALANCE:
     413            0 :         g_value_set_boxed(value, &priv->starting_noclosing_balance);
     414            0 :         break;
     415            1 :     case PROP_START_CLEARED_BALANCE:
     416            1 :         g_value_set_boxed(value, &priv->starting_cleared_balance);
     417            1 :         break;
     418            1 :     case PROP_START_RECONCILED_BALANCE:
     419            1 :         g_value_set_boxed(value, &priv->starting_reconciled_balance);
     420            1 :         break;
     421            3 :     case PROP_END_BALANCE:
     422            3 :         g_value_set_boxed(value, &priv->balance);
     423            3 :         break;
     424            0 :     case PROP_END_NOCLOSING_BALANCE:
     425            0 :         g_value_set_boxed(value, &priv->noclosing_balance);
     426            0 :         break;
     427            1 :     case PROP_END_CLEARED_BALANCE:
     428            1 :         g_value_set_boxed(value, &priv->cleared_balance);
     429            1 :         break;
     430            1 :     case PROP_END_RECONCILED_BALANCE:
     431            1 :         g_value_set_boxed(value, &priv->reconciled_balance);
     432            1 :         break;
     433            1 :     case PROP_POLICY:
     434              :         /* MAKE THIS A BOXED VALUE */
     435            1 :         g_value_set_pointer(value, priv->policy);
     436            1 :         break;
     437            1 :     case PROP_MARK:
     438            1 :         g_value_set_int(value, priv->mark);
     439            1 :         break;
     440            1 :     case PROP_TAX_RELATED:
     441            1 :         g_value_set_boolean(value, xaccAccountGetTaxRelated(account));
     442            1 :         break;
     443            1 :     case PROP_TAX_CODE:
     444            1 :         g_value_set_string(value, xaccAccountGetTaxUSCode(account));
     445            1 :         break;
     446            1 :     case PROP_TAX_SOURCE:
     447            1 :         g_value_set_string(value,
     448              :                            xaccAccountGetTaxUSPayerNameSource(account));
     449            1 :         break;
     450            1 :     case PROP_TAX_COPY_NUMBER:
     451            1 :         g_value_set_int64(value,
     452              :                           xaccAccountGetTaxUSCopyNumber(account));
     453            1 :         break;
     454           41 :     case PROP_HIDDEN:
     455           41 :         g_value_set_boolean(value, xaccAccountGetHidden(account));
     456           41 :         break;
     457            0 :     case PROP_AUTO_INTEREST:
     458            0 :         g_value_set_boolean (value, xaccAccountGetAutoInterest (account));
     459            0 :         break;
     460            1 :     case PROP_IS_OPENING_BALANCE:
     461            1 :         g_value_set_boolean(value, xaccAccountGetIsOpeningBalance(account));
     462            1 :         break;
     463           41 :     case PROP_PLACEHOLDER:
     464           41 :         g_value_set_boolean(value, xaccAccountGetPlaceholder(account));
     465           41 :         break;
     466            0 :     case PROP_FILTER:
     467            0 :         g_value_set_string(value, xaccAccountGetFilter(account));
     468            0 :         break;
     469            0 :     case PROP_SORT_ORDER:
     470            0 :         g_value_set_string(value, xaccAccountGetSortOrder(account));
     471            0 :         break;
     472            0 :     case PROP_SORT_REVERSED:
     473            0 :         g_value_set_boolean(value, xaccAccountGetSortReversed(account));
     474            0 :         break;
     475           56 :     case PROP_LOT_NEXT_ID:
     476              :         /* Pre-set the value in case the frame is empty */
     477           56 :         g_value_set_int64 (value, 0);
     478          224 :         qof_instance_get_path_kvp (QOF_INSTANCE (account), value, {KEY_LOT_MGMT, "next-id"});
     479           56 :         break;
     480            2 :     case PROP_ONLINE_ACCOUNT:
     481            6 :         qof_instance_get_path_kvp (QOF_INSTANCE (account), value, {KEY_ONLINE_ID});
     482            2 :         break;
     483            0 :     case PROP_IMP_APPEND_TEXT:
     484            0 :         g_value_set_boolean(value, xaccAccountGetAppendText(account));
     485            0 :         break;
     486            1 :     case PROP_OFX_INCOME_ACCOUNT:
     487            3 :         qof_instance_get_path_kvp (QOF_INSTANCE (account), value, {KEY_ASSOC_INCOME_ACCOUNT});
     488            1 :         break;
     489            1 :     case PROP_AB_ACCOUNT_ID:
     490            4 :         qof_instance_get_path_kvp (QOF_INSTANCE (account), value, {AB_KEY, AB_ACCOUNT_ID});
     491            1 :         break;
     492            1 :     case PROP_AB_ACCOUNT_UID:
     493            4 :         qof_instance_get_path_kvp (QOF_INSTANCE (account), value, {AB_KEY, AB_ACCOUNT_UID});
     494            1 :         break;
     495            1 :     case PROP_AB_BANK_CODE:
     496            4 :         qof_instance_get_path_kvp (QOF_INSTANCE (account), value, {AB_KEY, AB_BANK_CODE});
     497            1 :         break;
     498            3 :     case PROP_AB_TRANS_RETRIEVAL:
     499           12 :         qof_instance_get_path_kvp (QOF_INSTANCE (account), value, {AB_KEY, AB_TRANS_RETRIEVAL});
     500            3 :         break;
     501            0 :     default:
     502            0 :         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
     503            0 :         break;
     504              :     }
     505          121 : }
     506              : 
     507              : static void
     508        70774 : gnc_account_set_property (GObject         *object,
     509              :                           guint            prop_id,
     510              :                           const GValue    *value,
     511              :                           GParamSpec      *pspec)
     512              : {
     513              :     Account *account;
     514              :     gnc_numeric *number;
     515        70774 :     g_return_if_fail(GNC_IS_ACCOUNT(object));
     516        70774 :     account = GNC_ACCOUNT(object);
     517        70774 :     if (prop_id < PROP_RUNTIME_0)
     518          483 :         g_assert (qof_instance_get_editlevel(account));
     519              : 
     520        70774 :     switch (prop_id)
     521              :     {
     522           50 :     case PROP_NAME:
     523           50 :         xaccAccountSetName(account, g_value_get_string(value));
     524           50 :         break;
     525           46 :     case PROP_CODE:
     526           46 :         xaccAccountSetCode(account, g_value_get_string(value));
     527           46 :         break;
     528           42 :     case PROP_DESCRIPTION:
     529           42 :         xaccAccountSetDescription(account, g_value_get_string(value));
     530           42 :         break;
     531            0 :     case PROP_COLOR:
     532            0 :         xaccAccountSetColor(account, g_value_get_string(value));
     533            0 :         break;
     534            0 :     case PROP_NOTES:
     535            0 :         xaccAccountSetNotes(account, g_value_get_string(value));
     536            0 :         break;
     537           21 :     case PROP_TYPE:
     538              :         // NEED TO BE CONVERTED TO A G_TYPE_ENUM
     539           21 :         xaccAccountSetType(account, static_cast<GNCAccountType>(g_value_get_int(value)));
     540           21 :         break;
     541           29 :     case PROP_COMMODITY:
     542           29 :         xaccAccountSetCommodity(account, static_cast<gnc_commodity*>(g_value_get_object(value)));
     543           29 :         break;
     544           42 :     case PROP_COMMODITY_SCU:
     545           42 :         xaccAccountSetCommoditySCU(account, g_value_get_int(value));
     546           42 :         break;
     547           42 :     case PROP_NON_STD_SCU:
     548           42 :         xaccAccountSetNonStdSCU(account, g_value_get_boolean(value));
     549           42 :         break;
     550        35145 :     case PROP_SORT_DIRTY:
     551        35145 :         gnc_account_set_sort_dirty(account);
     552        35145 :         break;
     553        35145 :     case PROP_BALANCE_DIRTY:
     554        35145 :         gnc_account_set_balance_dirty(account);
     555        35145 :         break;
     556            1 :     case PROP_START_BALANCE:
     557            1 :         number = static_cast<gnc_numeric*>(g_value_get_boxed(value));
     558            1 :         gnc_account_set_start_balance(account, *number);
     559            1 :         break;
     560            0 :     case PROP_START_CLEARED_BALANCE:
     561            0 :         number = static_cast<gnc_numeric*>(g_value_get_boxed(value));
     562            0 :         gnc_account_set_start_cleared_balance(account, *number);
     563            0 :         break;
     564            0 :     case PROP_START_RECONCILED_BALANCE:
     565            0 :         number = static_cast<gnc_numeric*>(g_value_get_boxed(value));
     566            0 :         gnc_account_set_start_reconciled_balance(account, *number);
     567            0 :         break;
     568            0 :     case PROP_POLICY:
     569            0 :         gnc_account_set_policy(account, static_cast<GNCPolicy*>(g_value_get_pointer(value)));
     570            0 :         break;
     571            0 :     case PROP_MARK:
     572            0 :         xaccAccountSetMark(account, g_value_get_int(value));
     573            0 :         break;
     574            0 :     case PROP_TAX_RELATED:
     575            0 :         xaccAccountSetTaxRelated(account, g_value_get_boolean(value));
     576            0 :         break;
     577            0 :     case PROP_TAX_CODE:
     578            0 :         xaccAccountSetTaxUSCode(account, g_value_get_string(value));
     579            0 :         break;
     580            0 :     case PROP_TAX_SOURCE:
     581            0 :         xaccAccountSetTaxUSPayerNameSource(account,
     582              :                                            g_value_get_string(value));
     583            0 :         break;
     584            0 :     case PROP_TAX_COPY_NUMBER:
     585            0 :         xaccAccountSetTaxUSCopyNumber(account,
     586              :                                       g_value_get_int64(value));
     587            0 :         break;
     588           42 :     case PROP_HIDDEN:
     589           42 :         xaccAccountSetHidden(account, g_value_get_boolean(value));
     590           42 :         break;
     591            0 :     case PROP_AUTO_INTEREST:
     592            0 :         xaccAccountSetAutoInterest (account, g_value_get_boolean (value));
     593            0 :         break;
     594            0 :     case PROP_IS_OPENING_BALANCE:
     595            0 :         xaccAccountSetIsOpeningBalance (account, g_value_get_boolean (value));
     596            0 :         break;
     597           42 :     case PROP_PLACEHOLDER:
     598           42 :         xaccAccountSetPlaceholder(account, g_value_get_boolean(value));
     599           42 :         break;
     600            0 :     case PROP_FILTER:
     601            0 :         xaccAccountSetFilter(account, g_value_get_string(value));
     602            0 :         break;
     603            0 :     case PROP_SORT_ORDER:
     604            0 :         xaccAccountSetSortOrder(account, g_value_get_string(value));
     605            0 :         break;
     606            0 :     case PROP_SORT_REVERSED:
     607            0 :         xaccAccountSetSortReversed(account, g_value_get_boolean(value));
     608            0 :         break;
     609           56 :     case PROP_LOT_NEXT_ID:
     610          224 :         qof_instance_set_path_kvp (QOF_INSTANCE (account), value, {KEY_LOT_MGMT, "next-id"});
     611           56 :         break;
     612           64 :     case PROP_ONLINE_ACCOUNT:
     613          192 :         qof_instance_set_path_kvp (QOF_INSTANCE (account), value, {KEY_ONLINE_ID});
     614           64 :         break;
     615            0 :     case PROP_IMP_APPEND_TEXT:
     616            0 :         xaccAccountSetAppendText(account, g_value_get_boolean(value));
     617            0 :         break;
     618            2 :     case PROP_OFX_INCOME_ACCOUNT:
     619            6 :         qof_instance_set_path_kvp (QOF_INSTANCE (account), value, {KEY_ASSOC_INCOME_ACCOUNT});
     620            2 :         break;
     621            1 :     case PROP_AB_ACCOUNT_ID:
     622            4 :         qof_instance_set_path_kvp (QOF_INSTANCE (account), value, {AB_KEY, AB_ACCOUNT_ID});
     623            1 :         break;
     624            1 :     case PROP_AB_ACCOUNT_UID:
     625            4 :         qof_instance_set_path_kvp (QOF_INSTANCE (account), value, {AB_KEY, AB_ACCOUNT_UID});
     626            1 :         break;
     627            1 :     case PROP_AB_BANK_CODE:
     628            4 :         qof_instance_set_path_kvp (QOF_INSTANCE (account), value, {AB_KEY, AB_BANK_CODE});
     629            1 :         break;
     630            2 :     case PROP_AB_TRANS_RETRIEVAL:
     631            8 :         qof_instance_set_path_kvp (QOF_INSTANCE (account), value, {AB_KEY, AB_TRANS_RETRIEVAL});
     632            2 :         break;
     633            0 :     default:
     634            0 :         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
     635            0 :         break;
     636              :     }
     637          183 : }
     638              : 
     639              : static void
     640           68 : gnc_account_class_init (AccountClass *klass)
     641              : {
     642           68 :     GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
     643              : 
     644           68 :     gobject_class->dispose = gnc_account_dispose;
     645           68 :     gobject_class->finalize = gnc_account_finalize;
     646           68 :     gobject_class->set_property = gnc_account_set_property;
     647           68 :     gobject_class->get_property = gnc_account_get_property;
     648              : 
     649              :     g_object_class_install_property
     650           68 :     (gobject_class,
     651              :      PROP_NAME,
     652              :      g_param_spec_string ("name",
     653              :                           "Account Name",
     654              :                           "The accountName is an arbitrary string "
     655              :                           "assigned by the user.  It is intended to "
     656              :                           "a short, 5 to 30 character long string "
     657              :                           "that is displayed by the GUI as the "
     658              :                           "account mnemonic.  Account names may be "
     659              :                           "repeated. but no two accounts that share "
     660              :                           "a parent may have the same name.",
     661              :                           nullptr,
     662              :                           static_cast<GParamFlags>(G_PARAM_READWRITE)));
     663              : 
     664              :     g_object_class_install_property
     665           68 :     (gobject_class,
     666              :      PROP_FULL_NAME,
     667              :      g_param_spec_string ("fullname",
     668              :                           "Full Account Name",
     669              :                           "The name of the account concatenated with "
     670              :                           "all its parent account names to indicate "
     671              :                           "a unique account.",
     672              :                           nullptr,
     673              :                           static_cast<GParamFlags>(G_PARAM_READABLE)));
     674              : 
     675              :     g_object_class_install_property
     676           68 :     (gobject_class,
     677              :      PROP_CODE,
     678              :      g_param_spec_string ("code",
     679              :                           "Account Code",
     680              :                           "The account code is an arbitrary string "
     681              :                           "assigned by the user. It is intended to "
     682              :                           "be reporting code that is a synonym for "
     683              :                           "the accountName.",
     684              :                           nullptr,
     685              :                           static_cast<GParamFlags>(G_PARAM_READWRITE)));
     686              : 
     687              :     g_object_class_install_property
     688           68 :     (gobject_class,
     689              :      PROP_DESCRIPTION,
     690              :      g_param_spec_string ("description",
     691              :                           "Account Description",
     692              :                           "The account description is an arbitrary "
     693              :                           "string assigned by the user. It is intended "
     694              :                           "to be a longer, 1-5 sentence description of "
     695              :                           "what this account is all about.",
     696              :                           nullptr,
     697              :                           static_cast<GParamFlags>(G_PARAM_READWRITE)));
     698              : 
     699              :     g_object_class_install_property
     700           68 :     (gobject_class,
     701              :      PROP_COLOR,
     702              :      g_param_spec_string ("color",
     703              :                           "Account Color",
     704              :                           "The account color is a color string assigned "
     705              :                           "by the user. It is intended to highlight the "
     706              :                           "account based on the users wishes.",
     707              :                           nullptr,
     708              :                           static_cast<GParamFlags>(G_PARAM_READWRITE)));
     709              : 
     710              :     g_object_class_install_property
     711           68 :     (gobject_class,
     712              :      PROP_NOTES,
     713              :      g_param_spec_string ("notes",
     714              :                           "Account Notes",
     715              :                           "The account notes is an arbitrary provided "
     716              :                           "for the user to attach any other text that "
     717              :                           "they would like to associate with the account.",
     718              :                           nullptr,
     719              :                           static_cast<GParamFlags>(G_PARAM_READWRITE)));
     720              : 
     721              :     g_object_class_install_property
     722           68 :     (gobject_class,
     723              :      PROP_TYPE,
     724              :      g_param_spec_int ("type",
     725              :                        "Account Type",
     726              :                        "The account type, picked from the enumerated list "
     727              :                        "that includes ACCT_TYPE_BANK, ACCT_TYPE_STOCK, "
     728              :                        "ACCT_TYPE_CREDIT, ACCT_TYPE_INCOME, etc.",
     729              :                        ACCT_TYPE_NONE,
     730              :                        NUM_ACCOUNT_TYPES - 1,
     731              :                        ACCT_TYPE_BANK,
     732              :                        static_cast<GParamFlags>(G_PARAM_READWRITE)));
     733              : 
     734              :     g_object_class_install_property
     735           68 :     (gobject_class,
     736              :      PROP_COMMODITY,
     737              :      g_param_spec_object ("commodity",
     738              :                           "Commodity",
     739              :                           "The commodity field denotes the kind of "
     740              :                           "'stuff' stored  in this account, whether "
     741              :                           "it is USD, gold, stock, etc.",
     742              :                           GNC_TYPE_COMMODITY,
     743              :                           static_cast<GParamFlags>(G_PARAM_READWRITE)));
     744              : 
     745              :     g_object_class_install_property
     746           68 :     (gobject_class,
     747              :      PROP_COMMODITY_SCU,
     748              :      g_param_spec_int ("commodity-scu",
     749              :                        "Commodity SCU",
     750              :                        "The smallest fraction of the commodity that is "
     751              :                        "tracked.  This number is used as the denominator "
     752              :                        "value in 1/x, so a value of 100 says that the "
     753              :                        "commodity can be divided into hundredths.  E.G."
     754              :                        "1 USD can be divided into 100 cents.",
     755              :                        0,
     756              :                        G_MAXINT32,
     757              :                        GNC_COMMODITY_MAX_FRACTION,
     758              :                        static_cast<GParamFlags>(G_PARAM_READWRITE)));
     759              : 
     760              :     g_object_class_install_property
     761           68 :     (gobject_class,
     762              :      PROP_NON_STD_SCU,
     763              :      g_param_spec_boolean ("non-std-scu",
     764              :                            "Non-std SCU",
     765              :                            "TRUE if the account SCU doesn't match "
     766              :                            "the commodity SCU.  This indicates a case "
     767              :                            "where the two were accidentally set to "
     768              :                            "mismatched values in older versions of "
     769              :                            "GnuCash.",
     770              :                            FALSE,
     771              :                            static_cast<GParamFlags>(G_PARAM_READWRITE)));
     772              : 
     773              :     g_object_class_install_property
     774           68 :     (gobject_class,
     775              :      PROP_SORT_DIRTY,
     776              :      g_param_spec_boolean("sort-dirty",
     777              :                           "Sort Dirty",
     778              :                           "TRUE if the splits in the account needs to be "
     779              :                           "resorted.  This flag is set by the accounts "
     780              :                           "code for certain internal modifications, or "
     781              :                           "when external code calls the engine to say a "
     782              :                           "split has been modified in a way that may "
     783              :                           "affect the sort order of the account. Note: "
     784              :                           "This value can only be set to TRUE.",
     785              :                           FALSE,
     786              :                           static_cast<GParamFlags>(G_PARAM_READWRITE)));
     787              : 
     788              :     g_object_class_install_property
     789           68 :     (gobject_class,
     790              :      PROP_BALANCE_DIRTY,
     791              :      g_param_spec_boolean("balance-dirty",
     792              :                           "Balance Dirty",
     793              :                           "TRUE if the running balances in the account "
     794              :                           "needs to be recalculated.  This flag is set "
     795              :                           "by the accounts code for certain internal "
     796              :                           "modifications, or when external code calls "
     797              :                           "the engine to say a split has been modified. "
     798              :                           "Note: This value can only be set to TRUE.",
     799              :                           FALSE,
     800              :                           static_cast<GParamFlags>(G_PARAM_READWRITE)));
     801              : 
     802              :     g_object_class_install_property
     803           68 :     (gobject_class,
     804              :      PROP_START_BALANCE,
     805              :      g_param_spec_boxed("start-balance",
     806              :                         "Starting Balance",
     807              :                         "The starting balance for the account.  This "
     808              :                         "parameter is intended for use with backends that "
     809              :                         "do not return the complete list of splits for an "
     810              :                         "account, but rather return a partial list.  In "
     811              :                         "such a case, the backend will typically return "
     812              :                         "all of the splits after some certain date, and "
     813              :                         "the 'starting balance' will represent the "
     814              :                         "summation of the splits up to that date.",
     815              :                         GNC_TYPE_NUMERIC,
     816              :                         static_cast<GParamFlags>(G_PARAM_READWRITE)));
     817              : 
     818              :     g_object_class_install_property
     819           68 :     (gobject_class,
     820              :      PROP_START_NOCLOSING_BALANCE,
     821              :      g_param_spec_boxed("start-noclosing-balance",
     822              :                         "Starting No-closing Balance",
     823              :                         "The starting balance for the account, ignoring closing."
     824              :                         "This parameter is intended for use with backends "
     825              :                         "that do not return the complete list of splits "
     826              :                         "for an account, but rather return a partial "
     827              :                         "list.  In such a case, the backend will "
     828              :                         "typically return all of the splits after "
     829              :                         "some certain date, and the 'starting noclosing "
     830              :                         "balance' will represent the summation of the "
     831              :                         "splits up to that date, ignoring closing splits.",
     832              :                         GNC_TYPE_NUMERIC,
     833              :                         static_cast<GParamFlags>(G_PARAM_READWRITE)));
     834              : 
     835              :     g_object_class_install_property
     836           68 :     (gobject_class,
     837              :      PROP_START_CLEARED_BALANCE,
     838              :      g_param_spec_boxed("start-cleared-balance",
     839              :                         "Starting Cleared Balance",
     840              :                         "The starting cleared balance for the account.  "
     841              :                         "This parameter is intended for use with backends "
     842              :                         "that do not return the complete list of splits "
     843              :                         "for an account, but rather return a partial "
     844              :                         "list.  In such a case, the backend will "
     845              :                         "typically return all of the splits after "
     846              :                         "some certain date, and the 'starting cleared "
     847              :                         "balance' will represent the summation of the "
     848              :                         "splits up to that date.",
     849              :                         GNC_TYPE_NUMERIC,
     850              :                         static_cast<GParamFlags>(G_PARAM_READWRITE)));
     851              : 
     852              :     g_object_class_install_property
     853           68 :     (gobject_class,
     854              :      PROP_START_RECONCILED_BALANCE,
     855              :      g_param_spec_boxed("start-reconciled-balance",
     856              :                         "Starting Reconciled Balance",
     857              :                         "The starting reconciled balance for the "
     858              :                         "account.  This parameter is intended for use "
     859              :                         "with backends that do not return the complete "
     860              :                         "list of splits for an account, but rather return "
     861              :                         "a partial list.  In such a case, the backend "
     862              :                         "will typically return all of the splits after "
     863              :                         "some certain date, and the 'starting reconciled "
     864              :                         "balance' will represent the summation of the "
     865              :                         "splits up to that date.",
     866              :                         GNC_TYPE_NUMERIC,
     867              :                         static_cast<GParamFlags>(G_PARAM_READWRITE)));
     868              : 
     869              :     g_object_class_install_property
     870           68 :     (gobject_class,
     871              :      PROP_END_BALANCE,
     872              :      g_param_spec_boxed("end-balance",
     873              :                         "Ending Account Balance",
     874              :                         "This is the current ending balance for the "
     875              :                         "account.  It is computed from the sum of the "
     876              :                         "starting balance and all splits in the account.",
     877              :                         GNC_TYPE_NUMERIC,
     878              :                         G_PARAM_READABLE));
     879              : 
     880              :     g_object_class_install_property
     881           68 :     (gobject_class,
     882              :      PROP_END_NOCLOSING_BALANCE,
     883              :      g_param_spec_boxed("end-noclosing-balance",
     884              :                         "Ending Account Noclosing Balance",
     885              :                         "This is the current ending no-closing balance for "
     886              :                         "the account.  It is computed from the sum of the "
     887              :                         "starting balance and all cleared splits in the "
     888              :                         "account.",
     889              :                         GNC_TYPE_NUMERIC,
     890              :                         G_PARAM_READABLE));
     891              : 
     892              :     g_object_class_install_property
     893           68 :     (gobject_class,
     894              :      PROP_END_CLEARED_BALANCE,
     895              :      g_param_spec_boxed("end-cleared-balance",
     896              :                         "Ending Account Cleared Balance",
     897              :                         "This is the current ending cleared balance for "
     898              :                         "the account.  It is computed from the sum of the "
     899              :                         "starting balance and all cleared splits in the "
     900              :                         "account.",
     901              :                         GNC_TYPE_NUMERIC,
     902              :                         G_PARAM_READABLE));
     903              : 
     904              :     g_object_class_install_property
     905           68 :     (gobject_class,
     906              :      PROP_END_RECONCILED_BALANCE,
     907              :      g_param_spec_boxed("end-reconciled-balance",
     908              :                         "Ending Account Reconciled Balance",
     909              :                         "This is the current ending reconciled balance "
     910              :                         "for the account.  It is computed from the sum of "
     911              :                         "the starting balance and all reconciled splits "
     912              :                         "in the account.",
     913              :                         GNC_TYPE_NUMERIC,
     914              :                         static_cast<GParamFlags>(G_PARAM_READABLE)));
     915              : 
     916              :     g_object_class_install_property
     917           68 :     (gobject_class,
     918              :      PROP_POLICY,
     919              :      g_param_spec_pointer ("policy",
     920              :                            "Policy",
     921              :                            "The account lots policy.",
     922              :                            static_cast<GParamFlags>(G_PARAM_READWRITE)));
     923              : 
     924              :     g_object_class_install_property
     925           68 :     (gobject_class,
     926              :      PROP_MARK,
     927              :      g_param_spec_int ("acct-mark",
     928              :                        "Account Mark",
     929              :                        "Ipsum Lorem",
     930              :                        0,
     931              :                        G_MAXINT16,
     932              :                        0,
     933              :                        static_cast<GParamFlags>(G_PARAM_READWRITE)));
     934              : 
     935              :     g_object_class_install_property
     936           68 :     (gobject_class,
     937              :      PROP_TAX_RELATED,
     938              :      g_param_spec_boolean ("tax-related",
     939              :                            "Tax Related",
     940              :                            "Whether the account maps to an entry on an "
     941              :                            "income tax document.",
     942              :                            FALSE,
     943              :                            static_cast<GParamFlags>(G_PARAM_READWRITE)));
     944              : 
     945              :     g_object_class_install_property
     946           68 :     (gobject_class,
     947              :      PROP_IS_OPENING_BALANCE,
     948              :      g_param_spec_boolean ("opening-balance",
     949              :                            "Opening Balance",
     950              :                            "Whether the account holds opening balances",
     951              :                            FALSE,
     952              :                            static_cast<GParamFlags>(G_PARAM_READWRITE)));
     953              : 
     954              :     g_object_class_install_property
     955           68 :     (gobject_class,
     956              :      PROP_TAX_CODE,
     957              :      g_param_spec_string ("tax-code",
     958              :                           "Tax Code",
     959              :                           "This is the code for mapping an account to a "
     960              :                           "specific entry on a taxable document.  In the "
     961              :                           "United States it is used to transfer totals "
     962              :                           "into tax preparation software.",
     963              :                           nullptr,
     964              :                           static_cast<GParamFlags>(G_PARAM_READWRITE)));
     965              : 
     966              :     g_object_class_install_property
     967           68 :     (gobject_class,
     968              :      PROP_TAX_SOURCE,
     969              :      g_param_spec_string ("tax-source",
     970              :                           "Tax Source",
     971              :                           "This specifies where exported name comes from.",
     972              :                           nullptr,
     973              :                           static_cast<GParamFlags>(G_PARAM_READWRITE)));
     974              : 
     975              :     g_object_class_install_property
     976           68 :     (gobject_class,
     977              :      PROP_TAX_COPY_NUMBER,
     978              :      g_param_spec_int64 ("tax-copy-number",
     979              :                          "Tax Copy Number",
     980              :                          "This specifies the copy number of the tax "
     981              :                          "form/schedule.",
     982              :                          (gint64)1,
     983              :                          G_MAXINT64,
     984              :                          (gint64)1,
     985              :                          static_cast<GParamFlags>(G_PARAM_READWRITE)));
     986              : 
     987              :     g_object_class_install_property
     988           68 :     (gobject_class,
     989              :      PROP_HIDDEN,
     990              :      g_param_spec_boolean ("hidden",
     991              :                            "Hidden",
     992              :                            "Whether the account should be hidden in the  "
     993              :                            "account tree.",
     994              :                            FALSE,
     995              :                            static_cast<GParamFlags>(G_PARAM_READWRITE)));
     996              : 
     997              :     g_object_class_install_property
     998           68 :     (gobject_class,
     999              :      PROP_AUTO_INTEREST,
    1000              :      g_param_spec_boolean ("auto-interest-transfer",
    1001              :                            "Auto Interest",
    1002              :                            "Whether an interest transfer should be automatically  "
    1003              :                            "added before reconcile.",
    1004              :                            FALSE,
    1005              :                            static_cast<GParamFlags>(G_PARAM_READWRITE)));
    1006              : 
    1007              :     g_object_class_install_property
    1008           68 :     (gobject_class,
    1009              :      PROP_PLACEHOLDER,
    1010              :      g_param_spec_boolean ("placeholder",
    1011              :                            "Placeholder",
    1012              :                            "Whether the account is a placeholder account which does not "
    1013              :                            "allow transactions to be created, edited or deleted.",
    1014              :                            FALSE,
    1015              :                            static_cast<GParamFlags>(G_PARAM_READWRITE)));
    1016              : 
    1017              :     g_object_class_install_property
    1018           68 :     (gobject_class,
    1019              :      PROP_FILTER,
    1020              :      g_param_spec_string ("filter",
    1021              :                           "Account Filter",
    1022              :                           "The account filter is a value saved to allow "
    1023              :                           "filters to be recalled.",
    1024              :                           nullptr,
    1025              :                           static_cast<GParamFlags>(G_PARAM_READWRITE)));
    1026              : 
    1027              :     g_object_class_install_property
    1028           68 :     (gobject_class,
    1029              :      PROP_SORT_ORDER,
    1030              :      g_param_spec_string ("sort-order",
    1031              :                           "Account Sort Order",
    1032              :                           "The account sort order is a value saved to allow "
    1033              :                           "the sort order to be recalled.",
    1034              :                           nullptr,
    1035              :                           static_cast<GParamFlags>(G_PARAM_READWRITE)));
    1036              : 
    1037              :     g_object_class_install_property
    1038           68 :     (gobject_class,
    1039              :      PROP_SORT_REVERSED,
    1040              :      g_param_spec_boolean ("sort-reversed",
    1041              :                           "Account Sort Reversed",
    1042              :                           "Parameter to store whether the sort order is reversed or not.",
    1043              :                           FALSE,
    1044              :                           static_cast<GParamFlags>(G_PARAM_READWRITE)));
    1045              : 
    1046              :     g_object_class_install_property
    1047           68 :     (gobject_class,
    1048              :      PROP_LOT_NEXT_ID,
    1049              :      g_param_spec_int64 ("lot-next-id",
    1050              :                          "Lot Next ID",
    1051              :                          "Tracks the next id to use in gnc_lot_make_default.",
    1052              :                          (gint64)1,
    1053              :                          G_MAXINT64,
    1054              :                          (gint64)1,
    1055              :                          static_cast<GParamFlags>(G_PARAM_READWRITE)));
    1056              : 
    1057              :     g_object_class_install_property
    1058           68 :     (gobject_class,
    1059              :      PROP_ONLINE_ACCOUNT,
    1060              :      g_param_spec_string ("online-id",
    1061              :                           "Online Account ID",
    1062              :                           "The online account which corresponds to this "
    1063              :                           "account for OFX import",
    1064              :                           nullptr,
    1065              :                           static_cast<GParamFlags>(G_PARAM_READWRITE)));
    1066              : 
    1067              :     g_object_class_install_property
    1068           68 :     (gobject_class,
    1069              :      PROP_IMP_APPEND_TEXT,
    1070              :      g_param_spec_boolean ("import-append-text",
    1071              :                            "Import Append Text",
    1072              :                            "Saved state of Append checkbox for setting initial "
    1073              :                            "value next time this account is imported.",
    1074              :                            FALSE,
    1075              :                            static_cast<GParamFlags>(G_PARAM_READWRITE)));
    1076              : 
    1077           68 :      g_object_class_install_property(
    1078              :        gobject_class,
    1079              :        PROP_OFX_INCOME_ACCOUNT,
    1080              :         g_param_spec_boxed("ofx-income-account",
    1081              :                            "Associated income account",
    1082              :                            "Used by the OFX importer.",
    1083              :                            GNC_TYPE_GUID,
    1084              :                            static_cast<GParamFlags>(G_PARAM_READWRITE)));
    1085              : 
    1086              :     g_object_class_install_property
    1087           68 :     (gobject_class,
    1088              :      PROP_AB_ACCOUNT_ID,
    1089              :      g_param_spec_string ("ab-account-id",
    1090              :                           "AQBanking Account ID",
    1091              :                           "The AqBanking account which corresponds to this "
    1092              :                           "account for AQBanking import",
    1093              :                           nullptr,
    1094              :                           static_cast<GParamFlags>(G_PARAM_READWRITE)));
    1095              :     g_object_class_install_property
    1096           68 :     (gobject_class,
    1097              :      PROP_AB_BANK_CODE,
    1098              :      g_param_spec_string ("ab-bank-code",
    1099              :                           "AQBanking Bank Code",
    1100              :                           "The online account which corresponds to this "
    1101              :                           "account for AQBanking import",
    1102              :                           nullptr,
    1103              :                           static_cast<GParamFlags>(G_PARAM_READWRITE)));
    1104              : 
    1105              :     g_object_class_install_property
    1106           68 :     (gobject_class,
    1107              :      PROP_AB_ACCOUNT_UID,
    1108              :      g_param_spec_int64 ("ab-account-uid",
    1109              :                          "AQBanking Account UID",
    1110              :                          "Tracks the next id to use in gnc_lot_make_default.",
    1111              :                          (gint64)1,
    1112              :                          G_MAXINT64,
    1113              :                          (gint64)1,
    1114              :                          static_cast<GParamFlags>(G_PARAM_READWRITE)));
    1115              : 
    1116              :     g_object_class_install_property
    1117           68 :     (gobject_class,
    1118              :      PROP_AB_TRANS_RETRIEVAL,
    1119              :      g_param_spec_boxed("ab-trans-retrieval",
    1120              :                         "AQBanking Last Transaction Retrieval",
    1121              :                         "The time of the last transaction retrieval for this "
    1122              :                         "account.",
    1123              :                         GNC_TYPE_TIME64,
    1124              :                         static_cast<GParamFlags>(G_PARAM_READWRITE)));
    1125              : 
    1126           68 : }
    1127              : 
    1128              : static void
    1129         5071 : xaccInitAccount (Account * acc, QofBook *book)
    1130              : {
    1131         5071 :     ENTER ("book=%p\n", book);
    1132         5071 :     qof_instance_init_data (&acc->inst, GNC_ID_ACCOUNT, book);
    1133              : 
    1134         5071 :     LEAVE ("account=%p\n", acc);
    1135         5071 : }
    1136              : 
    1137              : /********************************************************************\
    1138              : \********************************************************************/
    1139              : 
    1140              : void
    1141         1260 : gnc_account_foreach_split (const Account *acc, std::function<void(Split*)> func)
    1142              : {
    1143         1260 :     gnc_account_foreach_split_between_dates (acc, {}, {}, false, func);
    1144         1260 : }
    1145              : 
    1146              : void
    1147            0 : gnc_account_foreach_split_until_date (const Account *acc, time64 end_date,
    1148              :                                       std::function<void(Split*)> f)
    1149              : {
    1150            0 :     gnc_account_foreach_split_between_dates (acc, {}, end_date, false, f);
    1151            0 : }
    1152              : 
    1153              : 
    1154              : Split*
    1155         2988 : gnc_account_find_split (const Account *acc, std::function<bool(const Split*)> predicate,
    1156              :                         bool reverse)
    1157              : {
    1158         2988 :     if (!GNC_IS_ACCOUNT (acc))
    1159            0 :         return nullptr;
    1160              : 
    1161         2988 :     const auto& splits{GET_PRIVATE(acc)->splits};
    1162         2988 :     if (reverse)
    1163              :     {
    1164         2940 :         auto latest = std::find_if(splits.rbegin(), splits.rend(), predicate);
    1165         2940 :         return (latest == splits.rend()) ? nullptr : *latest;
    1166              :     }
    1167              :     else
    1168              :     {
    1169           48 :         auto earliest = std::find_if(splits.begin(), splits.end(), predicate);
    1170           96 :         return (earliest == splits.end()) ? nullptr : *earliest;
    1171              :     }
    1172              : }
    1173              : 
    1174              : /********************************************************************\
    1175              : \********************************************************************/
    1176              : 
    1177              : QofBook *
    1178         1146 : gnc_account_get_book(const Account *account)
    1179              : {
    1180         1146 :     if (!account) return nullptr;
    1181         1146 :     return qof_instance_get_book(QOF_INSTANCE(account));
    1182              : }
    1183              : 
    1184              : /********************************************************************\
    1185              : \********************************************************************/
    1186              : 
    1187              : static Account *
    1188         2835 : gnc_coll_get_root_account (QofCollection *col)
    1189              : {
    1190         2835 :     if (!col) return nullptr;
    1191         2835 :     return static_cast<Account*>(qof_collection_get_data (col));
    1192              : }
    1193              : 
    1194              : static void
    1195          335 : gnc_coll_set_root_account (QofCollection *col, Account *root)
    1196              : {
    1197              :     AccountPrivate *rpriv;
    1198              :     Account *old_root;
    1199          335 :     if (!col) return;
    1200              : 
    1201          335 :     old_root = gnc_coll_get_root_account (col);
    1202          335 :     if (old_root == root) return;
    1203              : 
    1204              :     /* If the new root is already linked into the tree somewhere, then
    1205              :      * remove it from its current position before adding it at the
    1206              :      * top. */
    1207          334 :     rpriv = GET_PRIVATE(root);
    1208          334 :     if (rpriv->parent)
    1209              :     {
    1210           20 :         xaccAccountBeginEdit(root);
    1211           20 :         gnc_account_remove_child(rpriv->parent, root);
    1212           20 :         xaccAccountCommitEdit(root);
    1213              :     }
    1214              : 
    1215          334 :     qof_collection_set_data (col, root);
    1216              : 
    1217          334 :     if (old_root)
    1218              :     {
    1219           21 :         xaccAccountBeginEdit (old_root);
    1220           21 :         xaccAccountDestroy (old_root);
    1221              :     }
    1222              : }
    1223              : 
    1224              : Account *
    1225         2500 : gnc_book_get_root_account (QofBook *book)
    1226              : {
    1227              :     QofCollection *col;
    1228              :     Account *root;
    1229              : 
    1230         2500 :     if (!book) return nullptr;
    1231         2499 :     col = qof_book_get_collection (book, GNC_ID_ROOT_ACCOUNT);
    1232         2499 :     root = gnc_coll_get_root_account (col);
    1233         2499 :     if (root == nullptr && !qof_book_shutting_down(book))
    1234          208 :         root = gnc_account_create_root(book);
    1235         2499 :     return root;
    1236              : }
    1237              : 
    1238              : void
    1239          336 : gnc_book_set_root_account (QofBook *book, Account *root)
    1240              : {
    1241              :     QofCollection *col;
    1242          336 :     if (!book) return;
    1243              : 
    1244          336 :     if (root && gnc_account_get_book(root) != book)
    1245              :     {
    1246            1 :         PERR ("cannot mix and match books freely!");
    1247            1 :         return;
    1248              :     }
    1249              : 
    1250          335 :     col = qof_book_get_collection (book, GNC_ID_ROOT_ACCOUNT);
    1251          335 :     gnc_coll_set_root_account (col, root);
    1252              : }
    1253              : 
    1254              : /********************************************************************\
    1255              : \********************************************************************/
    1256              : 
    1257              : Account *
    1258         5070 : xaccMallocAccount (QofBook *book)
    1259              : {
    1260              :     Account *acc;
    1261              : 
    1262         5070 :     g_return_val_if_fail (book, nullptr);
    1263              : 
    1264         5070 :     acc = static_cast<Account*>(g_object_new (GNC_TYPE_ACCOUNT, nullptr));
    1265         5070 :     xaccInitAccount (acc, book);
    1266         5070 :     qof_event_gen (&acc->inst, QOF_EVENT_CREATE, nullptr);
    1267              : 
    1268         5070 :     return acc;
    1269              : }
    1270              : 
    1271              : Account *
    1272          292 : gnc_account_create_root (QofBook *book)
    1273              : {
    1274              :     Account *root;
    1275              :     AccountPrivate *rpriv;
    1276              : 
    1277          292 :     root = xaccMallocAccount(book);
    1278          292 :     rpriv = GET_PRIVATE(root);
    1279          292 :     xaccAccountBeginEdit(root);
    1280          292 :     rpriv->type = ACCT_TYPE_ROOT;
    1281          292 :     rpriv->accountName = qof_string_cache_replace(rpriv->accountName, "Root Account");
    1282          292 :     mark_account (root);
    1283          292 :     xaccAccountCommitEdit(root);
    1284          292 :     gnc_book_set_root_account(book, root);
    1285          292 :     return root;
    1286              : }
    1287              : 
    1288              : Account *
    1289            3 : xaccCloneAccount(const Account *from, QofBook *book)
    1290              : {
    1291              :     Account *ret;
    1292              :     AccountPrivate *from_priv, *priv;
    1293              : 
    1294            3 :     g_return_val_if_fail(GNC_IS_ACCOUNT(from), nullptr);
    1295            2 :     g_return_val_if_fail(QOF_IS_BOOK(book), nullptr);
    1296              : 
    1297            1 :     ENTER (" ");
    1298            1 :     ret = static_cast<Account*>(g_object_new (GNC_TYPE_ACCOUNT, nullptr));
    1299            1 :     g_return_val_if_fail (ret, nullptr);
    1300              : 
    1301            1 :     from_priv = GET_PRIVATE(from);
    1302            1 :     priv = GET_PRIVATE(ret);
    1303            1 :     xaccInitAccount (ret, book);
    1304              : 
    1305              :     /* Do not Begin/CommitEdit() here; give the caller
    1306              :      * a chance to fix things up, and let them do it.
    1307              :      * Also let caller issue the generate_event (EVENT_CREATE) */
    1308            1 :     priv->type = from_priv->type;
    1309              : 
    1310            1 :     priv->accountName = qof_string_cache_replace(priv->accountName, from_priv->accountName);
    1311            1 :     priv->accountCode = qof_string_cache_replace(priv->accountCode, from_priv->accountCode);
    1312            1 :     priv->description = qof_string_cache_replace(priv->description, from_priv->description);
    1313              : 
    1314            1 :     qof_instance_copy_kvp (QOF_INSTANCE (ret), QOF_INSTANCE (from));
    1315              : 
    1316              :     /* The new book should contain a commodity that matches
    1317              :      * the one in the old book. Find it, use it. */
    1318            1 :     priv->commodity = gnc_commodity_obtain_twin(from_priv->commodity, book);
    1319            1 :     gnc_commodity_increment_usage_count(priv->commodity);
    1320              : 
    1321            1 :     priv->commodity_scu = from_priv->commodity_scu;
    1322            1 :     priv->non_standard_scu = from_priv->non_standard_scu;
    1323              : 
    1324            1 :     qof_instance_set_dirty(&ret->inst);
    1325            1 :     LEAVE (" ");
    1326            1 :     return ret;
    1327              : }
    1328              : 
    1329              : /********************************************************************\
    1330              : \********************************************************************/
    1331              : 
    1332              : static void
    1333          358 : xaccFreeOneChildAccount (Account *acc)
    1334              : {
    1335              :     /* FIXME: this code is kind of hacky.  actually, all this code
    1336              :      * seems to assume that the account edit levels are all 1. */
    1337          358 :     if (qof_instance_get_editlevel(acc) == 0)
    1338          337 :         xaccAccountBeginEdit(acc);
    1339          358 :     xaccAccountDestroy(acc);
    1340          358 : }
    1341              : 
    1342              : static void
    1343         2229 : xaccFreeAccountChildren (Account *acc)
    1344              : {
    1345         2229 :     auto priv{GET_PRIVATE(acc)};
    1346              :     /* Copy the list since it will be modified */
    1347         2229 :     auto children = priv->children;
    1348         2229 :     std::for_each (children.begin(), children.end(), xaccFreeOneChildAccount);
    1349              : 
    1350              :     /* The foreach should have removed all the children already. */
    1351         2229 :     priv->children.clear();
    1352         2229 : }
    1353              : 
    1354              : /* The xaccFreeAccount() routine releases memory associated with the
    1355              :  * account.  It should never be called directly from user code;
    1356              :  * instead, the xaccAccountDestroy() routine should be used (because
    1357              :  * xaccAccountDestroy() has the correct commit semantics). */
    1358              : static void
    1359         2228 : xaccFreeAccount (Account *acc)
    1360              : {
    1361              :     AccountPrivate *priv;
    1362              :     GList *lp;
    1363              : 
    1364         2228 :     g_return_if_fail(GNC_IS_ACCOUNT(acc));
    1365              : 
    1366         2228 :     priv = GET_PRIVATE(acc);
    1367         2228 :     qof_event_gen (&acc->inst, QOF_EVENT_DESTROY, nullptr);
    1368              : 
    1369              :     /* Otherwise the lists below get munged while we're iterating
    1370              :      * them, possibly crashing.
    1371              :      */
    1372         2228 :     if (!qof_instance_get_destroying (acc))
    1373            1 :             qof_instance_set_destroying(acc, TRUE);
    1374              : 
    1375         2228 :     if (!priv->children.empty())
    1376              :     {
    1377            1 :         PERR (" instead of calling xaccFreeAccount(), please call\n"
    1378              :               " xaccAccountBeginEdit(); xaccAccountDestroy();\n");
    1379              : 
    1380              :         /* First, recursively free children, also frees list */
    1381            1 :         xaccFreeAccountChildren(acc);
    1382              :     }
    1383              : 
    1384              :     /* remove lots -- although these should be gone by now. */
    1385         2228 :     if (priv->lots)
    1386              :     {
    1387            1 :         PERR (" instead of calling xaccFreeAccount(), please call\n"
    1388              :               " xaccAccountBeginEdit(); xaccAccountDestroy();\n");
    1389              : 
    1390            4 :         for (lp = priv->lots; lp; lp = lp->next)
    1391              :         {
    1392            3 :             GNCLot *lot = static_cast<GNCLot*>(lp->data);
    1393            3 :             gnc_lot_destroy (lot);
    1394              :         }
    1395            1 :         g_list_free (priv->lots);
    1396            1 :         priv->lots = nullptr;
    1397              :     }
    1398              : 
    1399              :     /* Next, clean up the splits */
    1400              :     /* NB there shouldn't be any splits by now ... they should
    1401              :      * have been all been freed by CommitEdit().  We can remove this
    1402              :      * check once we know the warning isn't occurring any more. */
    1403         2228 :     if (!priv->splits.empty())
    1404              :     {
    1405            2 :         PERR (" instead of calling xaccFreeAccount(), please call\n"
    1406              :               " xaccAccountBeginEdit(); xaccAccountDestroy();\n");
    1407              : 
    1408            2 :         qof_instance_reset_editlevel(acc);
    1409              : 
    1410           10 :         for (auto s : priv->splits)
    1411              :         {
    1412            6 :             g_assert(xaccSplitGetAccount(s) == acc);
    1413            6 :             xaccSplitDestroy (s);
    1414              :         }
    1415              : /* Nothing here (or in xaccAccountCommitEdit) nullptrs priv->splits, so this asserts every time.
    1416              :         g_assert(priv->splits == nullptr);
    1417              : */
    1418              :     }
    1419              : 
    1420         2228 :     qof_string_cache_remove(priv->accountName);
    1421         2228 :     qof_string_cache_remove(priv->accountCode);
    1422         2228 :     qof_string_cache_remove(priv->description);
    1423         2228 :     priv->accountName = priv->accountCode = priv->description = nullptr;
    1424              : 
    1425              :     /* zero out values, just in case stray
    1426              :      * pointers are pointing here. */
    1427              : 
    1428         2228 :     priv->last_num = nullptr;
    1429         2228 :     priv->tax_us_code = nullptr;
    1430         2228 :     priv->tax_us_pns = nullptr;
    1431         2228 :     priv->color = nullptr;
    1432         2228 :     priv->sort_order = nullptr;
    1433         2228 :     priv->notes = nullptr;
    1434         2228 :     priv->filter = nullptr;
    1435              : 
    1436         2228 :     priv->parent = nullptr;
    1437              : 
    1438         2228 :     priv->balance  = gnc_numeric_zero();
    1439         2228 :     priv->noclosing_balance = gnc_numeric_zero();
    1440         2228 :     priv->cleared_balance = gnc_numeric_zero();
    1441         2228 :     priv->reconciled_balance = gnc_numeric_zero();
    1442              : 
    1443         2228 :     priv->type = ACCT_TYPE_NONE;
    1444         2228 :     gnc_commodity_decrement_usage_count(priv->commodity);
    1445         2228 :     priv->commodity = nullptr;
    1446              : 
    1447         2228 :     priv->balance_dirty = FALSE;
    1448         2228 :     priv->has_stock_split = false;
    1449         2228 :     priv->sort_dirty = FALSE;
    1450         2228 :     priv->splits.~SplitsVec();
    1451         2228 :     priv->children.~AccountVec();
    1452         2228 :     g_hash_table_destroy (priv->splits_hash);
    1453              : 
    1454              :     /* qof_instance_release (&acc->inst); */
    1455         2228 :     g_object_unref(acc);
    1456              : }
    1457              : 
    1458              : /********************************************************************\
    1459              :  * transactional routines
    1460              : \********************************************************************/
    1461              : 
    1462              : void
    1463        34811 : xaccAccountBeginEdit (Account *acc)
    1464              : {
    1465        34811 :     g_return_if_fail(acc);
    1466        34811 :     qof_begin_edit(&acc->inst);
    1467              : }
    1468              : 
    1469        12592 : static void on_done(QofInstance *inst)
    1470              : {
    1471              :     /* old event style */
    1472        12592 :     qof_event_gen (inst, QOF_EVENT_MODIFY, nullptr);
    1473        12592 : }
    1474              : 
    1475            0 : static void on_err (QofInstance *inst, QofBackendError errcode)
    1476              : {
    1477            0 :     PERR("commit error: %d", errcode);
    1478            0 :     gnc_engine_signal_commit_error( errcode );
    1479            0 : }
    1480              : 
    1481         2227 : static void acc_free (QofInstance *inst)
    1482              : {
    1483              :     AccountPrivate *priv;
    1484         2227 :     Account *acc = (Account *) inst;
    1485              : 
    1486         2227 :     priv = GET_PRIVATE(acc);
    1487         2227 :     if (priv->parent)
    1488         1803 :         gnc_account_remove_child(priv->parent, acc);
    1489         2227 :     xaccFreeAccount(acc);
    1490         2227 : }
    1491              : 
    1492              : static void
    1493           78 : destroy_pending_splits_for_account(QofInstance *ent, gpointer acc)
    1494              : {
    1495           78 :     Transaction *trans = (Transaction *) ent;
    1496              :     Split *split;
    1497              : 
    1498           78 :     if (xaccTransIsOpen(trans))
    1499           29 :         while ((split = xaccTransFindSplitByAccount(trans, static_cast<Account*>(acc))))
    1500           10 :             xaccSplitDestroy(split);
    1501           78 : }
    1502              : 
    1503              : void
    1504        34086 : xaccAccountCommitEdit (Account *acc)
    1505              : {
    1506              :     AccountPrivate *priv;
    1507              :     QofBook *book;
    1508              : 
    1509        34086 :     g_return_if_fail(acc);
    1510        34086 :     if (!qof_commit_edit(&acc->inst)) return;
    1511              : 
    1512              :     /* If marked for deletion, get rid of subaccounts first,
    1513              :      * and then the splits ... */
    1514        14819 :     priv = GET_PRIVATE(acc);
    1515        14819 :     if (qof_instance_get_destroying(acc))
    1516              :     {
    1517              :         QofCollection *col;
    1518              : 
    1519         2227 :         qof_instance_increase_editlevel(acc);
    1520              : 
    1521              :         /* First, recursively free children */
    1522         2227 :         xaccFreeAccountChildren(acc);
    1523              : 
    1524         2227 :         PINFO ("freeing splits for account %p (%s)",
    1525              :                acc, priv->accountName ? priv->accountName : "(null)");
    1526              : 
    1527         2227 :         book = qof_instance_get_book(acc);
    1528              : 
    1529              :         /* If book is shutting down, just clear the split list.  The splits
    1530              :            themselves will be destroyed by the transaction code */
    1531         2227 :         if (!qof_book_shutting_down(book))
    1532              :         {
    1533              :             // We need to delete in reverse order so that the vector's iterators aren't invalidated.
    1534          516 :             for_each(priv->splits.rbegin(), priv->splits.rend(), [](Split *s) {
    1535            3 :                 xaccSplitDestroy (s); });
    1536              :         }
    1537              :         else
    1538              :         {
    1539         1711 :             priv->splits.clear();
    1540         1711 :             g_hash_table_remove_all (priv->splits_hash);
    1541              :         }
    1542              : 
    1543              :         /* It turns out there's a case where this assertion does not hold:
    1544              :            When the user tries to delete an Imbalance account, while also
    1545              :            deleting all the splits in it.  The splits will just get
    1546              :            recreated and put right back into the same account!
    1547              : 
    1548              :            g_assert(priv->splits == nullptr || qof_book_shutting_down(acc->inst.book));
    1549              :         */
    1550              : 
    1551         2227 :         if (!qof_book_shutting_down(book))
    1552              :         {
    1553          516 :             col = qof_book_get_collection(book, GNC_ID_TRANS);
    1554          516 :             qof_collection_foreach(col, destroy_pending_splits_for_account, acc);
    1555              : 
    1556              :             /* the lots should be empty by now */
    1557          519 :             for (auto lp = priv->lots; lp; lp = lp->next)
    1558              :             {
    1559            3 :                 GNCLot *lot = static_cast<GNCLot*>(lp->data);
    1560            3 :                 gnc_lot_destroy (lot);
    1561              :             }
    1562              :         }
    1563         2227 :         g_list_free(priv->lots);
    1564         2227 :         priv->lots = nullptr;
    1565              : 
    1566         2227 :         qof_instance_set_dirty(&acc->inst);
    1567         2227 :         qof_instance_decrease_editlevel(acc);
    1568              :     }
    1569              :     else
    1570              :     {
    1571        12592 :         xaccAccountBringUpToDate(acc);
    1572              :     }
    1573              : 
    1574        14819 :     qof_commit_edit_part2(&acc->inst, on_err, on_done, acc_free);
    1575              : }
    1576              : 
    1577              : void
    1578         2282 : xaccAccountDestroy (Account *acc)
    1579              : {
    1580         2282 :     g_return_if_fail(GNC_IS_ACCOUNT(acc));
    1581              : 
    1582         2282 :     qof_instance_set_destroying(acc, TRUE);
    1583              : 
    1584         2282 :     xaccAccountCommitEdit (acc);
    1585              : }
    1586              : 
    1587              : void
    1588            0 : xaccAccountDestroyAllTransactions(Account *acc)
    1589              : {
    1590            0 :     auto priv = GET_PRIVATE(acc);
    1591            0 :     std::vector<Transaction*> transactions;
    1592            0 :     transactions.reserve(priv->splits.size());
    1593            0 :     std::transform(priv->splits.begin(), priv->splits.end(),
    1594              :                    back_inserter(transactions),
    1595            0 :                    [](auto split) { return split->parent; });
    1596            0 :     std::stable_sort(transactions.begin(), transactions.end());
    1597            0 :     transactions.erase(std::unique(transactions.begin(), transactions.end()),
    1598            0 :                        transactions.end());
    1599            0 :     qof_event_suspend();
    1600            0 :     std::for_each(transactions.rbegin(), transactions.rend(),
    1601            0 :                   [](auto trans) { xaccTransDestroy (trans); });
    1602            0 :     qof_event_resume();
    1603            0 : }
    1604              : 
    1605              : /********************************************************************\
    1606              : \********************************************************************/
    1607              : 
    1608              : static gboolean
    1609           42 : xaccAcctChildrenEqual(const AccountVec& na,
    1610              :                       const AccountVec& nb,
    1611              :                       gboolean check_guids)
    1612              : {
    1613           42 :     if (na.size() != nb.size())
    1614              :     {
    1615            0 :         PINFO ("Accounts have different numbers of children");
    1616            0 :         return (FALSE);
    1617              :     }
    1618              : 
    1619           99 :     for (auto aa : na)
    1620              :     {
    1621           15 :         auto it_b = std::find_if (nb.begin(), nb.end(), [aa](auto ab) -> bool
    1622              :         {
    1623           28 :             if (!aa) return (!ab);
    1624           28 :             if (!ab) return false;
    1625           28 :             auto code_a{GET_PRIVATE(aa)->accountCode};
    1626           28 :             auto code_b{GET_PRIVATE(ab)->accountCode};
    1627           28 :             if ((code_a && *code_a) || (code_b && *code_b)) return !g_strcmp0 (code_a, code_b);
    1628           24 :             return !g_strcmp0 (GET_PRIVATE(aa)->accountName, GET_PRIVATE(ab)->accountName);
    1629              :         });
    1630              : 
    1631           30 :         if (it_b == nb.end())
    1632              :         {
    1633            0 :             PINFO ("Unable to find matching child account.");
    1634            0 :             return FALSE;
    1635              :         }
    1636           15 :         else if (auto ab = *it_b; !xaccAccountEqual(aa, ab, check_guids))
    1637              :         {
    1638              :             char sa[GUID_ENCODING_LENGTH + 1];
    1639              :             char sb[GUID_ENCODING_LENGTH + 1];
    1640              : 
    1641            0 :             guid_to_string_buff (xaccAccountGetGUID (aa), sa);
    1642            0 :             guid_to_string_buff (xaccAccountGetGUID (ab), sb);
    1643              : 
    1644            0 :             PWARN ("accounts %s and %s differ", sa, sb);
    1645              : 
    1646            0 :             return(FALSE);
    1647              :         }
    1648              :     }
    1649              : 
    1650           42 :     return(TRUE);
    1651              : }
    1652              : 
    1653              : gboolean
    1654           43 : xaccAccountEqual(const Account *aa, const Account *ab, gboolean check_guids)
    1655              : {
    1656              :     AccountPrivate *priv_aa, *priv_ab;
    1657              : 
    1658           43 :     if (!aa && !ab) return TRUE;
    1659              : 
    1660           42 :     g_return_val_if_fail(GNC_IS_ACCOUNT(aa), FALSE);
    1661           42 :     g_return_val_if_fail(GNC_IS_ACCOUNT(ab), FALSE);
    1662              : 
    1663           42 :     priv_aa = GET_PRIVATE(aa);
    1664           42 :     priv_ab = GET_PRIVATE(ab);
    1665           42 :     if (priv_aa->type != priv_ab->type)
    1666              :     {
    1667            0 :         PWARN ("types differ: %d vs %d", priv_aa->type, priv_ab->type);
    1668            0 :         return FALSE;
    1669              :     }
    1670              : 
    1671           42 :     if (g_strcmp0(priv_aa->accountName, priv_ab->accountName) != 0)
    1672              :     {
    1673            0 :         PWARN ("names differ: %s vs %s", priv_aa->accountName, priv_ab->accountName);
    1674            0 :         return FALSE;
    1675              :     }
    1676              : 
    1677           42 :     if (g_strcmp0(priv_aa->accountCode, priv_ab->accountCode) != 0)
    1678              :     {
    1679            0 :         PWARN ("codes differ: %s vs %s", priv_aa->accountCode, priv_ab->accountCode);
    1680            0 :         return FALSE;
    1681              :     }
    1682              : 
    1683           42 :     if (g_strcmp0(priv_aa->description, priv_ab->description) != 0)
    1684              :     {
    1685            0 :         PWARN ("descriptions differ: %s vs %s", priv_aa->description, priv_ab->description);
    1686            0 :         return FALSE;
    1687              :     }
    1688              : 
    1689           42 :     if (!gnc_commodity_equal(priv_aa->commodity, priv_ab->commodity))
    1690              :     {
    1691            0 :         PWARN ("commodities differ");
    1692            0 :         return FALSE;
    1693              :     }
    1694              : 
    1695           42 :     if (check_guids)
    1696              :     {
    1697           24 :         if (qof_instance_guid_compare(aa, ab) != 0)
    1698              :         {
    1699            0 :             PWARN ("GUIDs differ");
    1700            0 :             return FALSE;
    1701              :         }
    1702              :     }
    1703              : 
    1704           42 :     if (qof_instance_compare_kvp (QOF_INSTANCE (aa), QOF_INSTANCE (ab)) != 0)
    1705              :     {
    1706              :         char *frame_a;
    1707              :         char *frame_b;
    1708              : 
    1709            0 :         frame_a = qof_instance_kvp_as_string (QOF_INSTANCE (aa));
    1710            0 :         frame_b = qof_instance_kvp_as_string (QOF_INSTANCE (ab));
    1711              : 
    1712            0 :         PWARN ("kvp frames differ:\n%s\n\nvs\n\n%s", frame_a, frame_b);
    1713              : 
    1714            0 :         g_free (frame_a);
    1715            0 :         g_free (frame_b);
    1716              : 
    1717            0 :         return FALSE;
    1718              :     }
    1719              : 
    1720           42 :     if (!gnc_numeric_equal(priv_aa->starting_balance, priv_ab->starting_balance))
    1721              :     {
    1722              :         char *str_a;
    1723              :         char *str_b;
    1724              : 
    1725            0 :         str_a = gnc_numeric_to_string(priv_aa->starting_balance);
    1726            0 :         str_b = gnc_numeric_to_string(priv_ab->starting_balance);
    1727              : 
    1728            0 :         PWARN ("starting balances differ: %s vs %s", str_a, str_b);
    1729              : 
    1730            0 :         g_free (str_a);
    1731            0 :         g_free (str_b);
    1732              : 
    1733            0 :         return FALSE;
    1734              :     }
    1735              : 
    1736           42 :     if (!gnc_numeric_equal(priv_aa->starting_noclosing_balance,
    1737              :                            priv_ab->starting_noclosing_balance))
    1738              :     {
    1739              :         char *str_a;
    1740              :         char *str_b;
    1741              : 
    1742            0 :         str_a = gnc_numeric_to_string(priv_aa->starting_noclosing_balance);
    1743            0 :         str_b = gnc_numeric_to_string(priv_ab->starting_noclosing_balance);
    1744              : 
    1745            0 :         PWARN ("starting noclosing balances differ: %s vs %s", str_a, str_b);
    1746              : 
    1747            0 :         g_free (str_a);
    1748            0 :         g_free (str_b);
    1749              : 
    1750            0 :         return FALSE;
    1751              :     }
    1752           42 :     if (!gnc_numeric_equal(priv_aa->starting_cleared_balance,
    1753              :                            priv_ab->starting_cleared_balance))
    1754              :     {
    1755              :         char *str_a;
    1756              :         char *str_b;
    1757              : 
    1758            0 :         str_a = gnc_numeric_to_string(priv_aa->starting_cleared_balance);
    1759            0 :         str_b = gnc_numeric_to_string(priv_ab->starting_cleared_balance);
    1760              : 
    1761            0 :         PWARN ("starting cleared balances differ: %s vs %s", str_a, str_b);
    1762              : 
    1763            0 :         g_free (str_a);
    1764            0 :         g_free (str_b);
    1765              : 
    1766            0 :         return FALSE;
    1767              :     }
    1768              : 
    1769           42 :     if (!gnc_numeric_equal(priv_aa->starting_reconciled_balance,
    1770              :                            priv_ab->starting_reconciled_balance))
    1771              :     {
    1772              :         char *str_a;
    1773              :         char *str_b;
    1774              : 
    1775            0 :         str_a = gnc_numeric_to_string(priv_aa->starting_reconciled_balance);
    1776            0 :         str_b = gnc_numeric_to_string(priv_ab->starting_reconciled_balance);
    1777              : 
    1778            0 :         PWARN ("starting reconciled balances differ: %s vs %s", str_a, str_b);
    1779              : 
    1780            0 :         g_free (str_a);
    1781            0 :         g_free (str_b);
    1782              : 
    1783            0 :         return FALSE;
    1784              :     }
    1785              : 
    1786           42 :     if (!gnc_numeric_equal(priv_aa->balance, priv_ab->balance))
    1787              :     {
    1788              :         char *str_a;
    1789              :         char *str_b;
    1790              : 
    1791            0 :         str_a = gnc_numeric_to_string(priv_aa->balance);
    1792            0 :         str_b = gnc_numeric_to_string(priv_ab->balance);
    1793              : 
    1794            0 :         PWARN ("balances differ: %s vs %s", str_a, str_b);
    1795              : 
    1796            0 :         g_free (str_a);
    1797            0 :         g_free (str_b);
    1798              : 
    1799            0 :         return FALSE;
    1800              :     }
    1801              : 
    1802           42 :     if (!gnc_numeric_equal(priv_aa->noclosing_balance, priv_ab->noclosing_balance))
    1803              :     {
    1804              :         char *str_a;
    1805              :         char *str_b;
    1806              : 
    1807            0 :         str_a = gnc_numeric_to_string(priv_aa->noclosing_balance);
    1808            0 :         str_b = gnc_numeric_to_string(priv_ab->noclosing_balance);
    1809              : 
    1810            0 :         PWARN ("noclosing balances differ: %s vs %s", str_a, str_b);
    1811              : 
    1812            0 :         g_free (str_a);
    1813            0 :         g_free (str_b);
    1814              : 
    1815            0 :         return FALSE;
    1816              :     }
    1817           42 :     if (!gnc_numeric_equal(priv_aa->cleared_balance, priv_ab->cleared_balance))
    1818              :     {
    1819              :         char *str_a;
    1820              :         char *str_b;
    1821              : 
    1822            0 :         str_a = gnc_numeric_to_string(priv_aa->cleared_balance);
    1823            0 :         str_b = gnc_numeric_to_string(priv_ab->cleared_balance);
    1824              : 
    1825            0 :         PWARN ("cleared balances differ: %s vs %s", str_a, str_b);
    1826              : 
    1827            0 :         g_free (str_a);
    1828            0 :         g_free (str_b);
    1829              : 
    1830            0 :         return FALSE;
    1831              :     }
    1832              : 
    1833           42 :     if (!gnc_numeric_equal(priv_aa->reconciled_balance, priv_ab->reconciled_balance))
    1834              :     {
    1835              :         char *str_a;
    1836              :         char *str_b;
    1837              : 
    1838            0 :         str_a = gnc_numeric_to_string(priv_aa->reconciled_balance);
    1839            0 :         str_b = gnc_numeric_to_string(priv_ab->reconciled_balance);
    1840              : 
    1841            0 :         PWARN ("reconciled balances differ: %s vs %s", str_a, str_b);
    1842              : 
    1843            0 :         g_free (str_a);
    1844            0 :         g_free (str_b);
    1845              : 
    1846            0 :         return FALSE;
    1847              :     }
    1848              : 
    1849              :     /* no parent; always compare downwards. */
    1850              : 
    1851           42 :     if (!std::equal (priv_aa->splits.begin(), priv_aa->splits.end(),
    1852              :                      priv_ab->splits.begin(), priv_ab->splits.end(),
    1853           15 :                      [check_guids](auto sa, auto sb)
    1854           15 :                      { return xaccSplitEqual(sa, sb, check_guids, true, false); }))
    1855              :     {
    1856            0 :         PWARN ("splits differ");
    1857            0 :         return false;
    1858              :     }
    1859              : 
    1860           42 :     if (!xaccAcctChildrenEqual(priv_aa->children, priv_ab->children, check_guids))
    1861              :     {
    1862            0 :         PWARN ("children differ");
    1863            0 :         return FALSE;
    1864              :     }
    1865              : 
    1866           42 :     return(TRUE);
    1867              : }
    1868              : 
    1869              : /********************************************************************\
    1870              : \********************************************************************/
    1871              : void
    1872        35145 : gnc_account_set_sort_dirty (Account *acc)
    1873              : {
    1874              :     AccountPrivate *priv;
    1875              : 
    1876        35145 :     g_return_if_fail(GNC_IS_ACCOUNT(acc));
    1877              : 
    1878        35145 :     if (qof_instance_get_destroying(acc))
    1879            0 :         return;
    1880              : 
    1881        35145 :     priv = GET_PRIVATE(acc);
    1882        35145 :     priv->sort_dirty = TRUE;
    1883              : }
    1884              : 
    1885              : void
    1886        35145 : gnc_account_set_balance_dirty (Account *acc)
    1887              : {
    1888              :     AccountPrivate *priv;
    1889              : 
    1890        35145 :     g_return_if_fail(GNC_IS_ACCOUNT(acc));
    1891              : 
    1892        35145 :     if (qof_instance_get_destroying(acc))
    1893            0 :         return;
    1894              : 
    1895        35145 :     priv = GET_PRIVATE(acc);
    1896        35145 :     priv->balance_dirty = TRUE;
    1897              : }
    1898              : 
    1899            0 : void gnc_account_set_defer_bal_computation (Account *acc, gboolean defer)
    1900              : {
    1901              :     AccountPrivate *priv;
    1902              : 
    1903            0 :     g_return_if_fail (GNC_IS_ACCOUNT (acc));
    1904              : 
    1905            0 :     if (qof_instance_get_destroying (acc))
    1906            0 :         return;
    1907              : 
    1908            0 :     priv = GET_PRIVATE (acc);
    1909            0 :     priv->defer_bal_computation = defer;
    1910              : }
    1911              : 
    1912            0 : gboolean gnc_account_get_defer_bal_computation (Account *acc)
    1913              : {
    1914              :     AccountPrivate *priv;
    1915            0 :     if (!acc)
    1916            0 :         return false;
    1917            0 :     priv = GET_PRIVATE (acc);
    1918            0 :     return priv->defer_bal_computation;
    1919              : }
    1920              : 
    1921              : 
    1922              : /********************************************************************\
    1923              : \********************************************************************/
    1924              : 
    1925       538778 : static bool split_cmp_less (const Split* a, const Split* b)
    1926              : {
    1927       538778 :     return xaccSplitOrder (a, b) < 0;
    1928              : }
    1929              : 
    1930              : gboolean
    1931         7377 : gnc_account_insert_split (Account *acc, Split *s)
    1932              : {
    1933              :     AccountPrivate *priv;
    1934              : 
    1935         7377 :     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
    1936         7376 :     g_return_val_if_fail(GNC_IS_SPLIT(s), FALSE);
    1937              : 
    1938         7375 :     priv = GET_PRIVATE(acc);
    1939         7375 :     if (!g_hash_table_add (priv->splits_hash, s))
    1940            2 :         return false;
    1941              : 
    1942         7373 :     priv->splits.push_back (s);
    1943              : 
    1944         7373 :     if (qof_instance_get_editlevel(acc) == 0)
    1945         5436 :         std::sort (priv->splits.begin(), priv->splits.end(), split_cmp_less);
    1946              :     else
    1947         1937 :         priv->sort_dirty = true;
    1948              : 
    1949              :     //FIXME: find better event
    1950         7373 :     qof_event_gen (&acc->inst, QOF_EVENT_MODIFY, nullptr);
    1951              :     /* Also send an event based on the account */
    1952         7373 :     qof_event_gen(&acc->inst, GNC_EVENT_ITEM_ADDED, s);
    1953              : 
    1954         7373 :     priv->balance_dirty = TRUE;
    1955              : //  DRH: Should the below be added? It is present in the delete path.
    1956              : //  xaccAccountRecomputeBalance(acc);
    1957         7373 :     return TRUE;
    1958              : }
    1959              : 
    1960              : gboolean
    1961        10003 : gnc_account_remove_split (Account *acc, Split *s)
    1962              : {
    1963              :     AccountPrivate *priv;
    1964              : 
    1965        10003 :     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
    1966        10003 :     g_return_val_if_fail(GNC_IS_SPLIT(s), FALSE);
    1967              : 
    1968        10003 :     priv = GET_PRIVATE(acc);
    1969              : 
    1970        10003 :     if (!g_hash_table_remove (priv->splits_hash, s))
    1971         4942 :         return false;
    1972              : 
    1973              :     // shortcut pruning the last element. this is the most common
    1974              :     // remove_split operation during UI or book shutdown.
    1975         5061 :     if (s == priv->splits.back())
    1976         5020 :         priv->splits.pop_back();
    1977              :     else
    1978           82 :         priv->splits.erase (std::remove (priv->splits.begin(), priv->splits.end(), s),
    1979           82 :                             priv->splits.end());
    1980              : 
    1981              :     //FIXME: find better event type
    1982         5061 :     qof_event_gen(&acc->inst, QOF_EVENT_MODIFY, nullptr);
    1983              :     // And send the account-based event, too
    1984         5061 :     qof_event_gen(&acc->inst, GNC_EVENT_ITEM_REMOVED, s);
    1985              : 
    1986         5061 :     priv->balance_dirty = TRUE;
    1987         5061 :     xaccAccountRecomputeBalance(acc);
    1988         5061 :     return TRUE;
    1989              : }
    1990              : 
    1991              : void
    1992        15634 : xaccAccountSortSplits (Account *acc, gboolean force)
    1993              : {
    1994              :     AccountPrivate *priv;
    1995              : 
    1996        15634 :     g_return_if_fail(GNC_IS_ACCOUNT(acc));
    1997              : 
    1998        15634 :     priv = GET_PRIVATE(acc);
    1999        15634 :     if (!priv->sort_dirty || (!force && qof_instance_get_editlevel(acc) > 0))
    2000        11882 :         return;
    2001         3752 :     std::sort (priv->splits.begin(), priv->splits.end(), split_cmp_less);
    2002         3752 :     priv->sort_dirty = FALSE;
    2003         3752 :     priv->balance_dirty = TRUE;
    2004              : }
    2005              : 
    2006              : static void
    2007        12592 : xaccAccountBringUpToDate(Account *acc)
    2008              : {
    2009        12592 :     if (!acc) return;
    2010              : 
    2011              :     /* if a re-sort happens here, then everything will update, so the
    2012              :        cost basis and balance calls are no-ops */
    2013        12592 :     xaccAccountSortSplits(acc, FALSE);
    2014        12592 :     xaccAccountRecomputeBalance(acc);
    2015              : }
    2016              : 
    2017              : /********************************************************************\
    2018              : \********************************************************************/
    2019              : 
    2020              : void
    2021         1308 : xaccAccountSetGUID (Account *acc, const GncGUID *guid)
    2022              : {
    2023         1308 :     g_return_if_fail(GNC_IS_ACCOUNT(acc));
    2024         1308 :     g_return_if_fail(guid);
    2025              : 
    2026              :     /* XXX this looks fishy and weird to me ... */
    2027         1308 :     PINFO("acct=%p", acc);
    2028         1308 :     xaccAccountBeginEdit (acc);
    2029         1308 :     qof_instance_set_guid (&acc->inst, guid);
    2030         1308 :     qof_instance_set_dirty(&acc->inst);
    2031         1308 :     xaccAccountCommitEdit (acc);
    2032              : }
    2033              : 
    2034              : /********************************************************************\
    2035              : \********************************************************************/
    2036              : 
    2037              : Account *
    2038         4278 : xaccAccountLookup (const GncGUID *guid, QofBook *book)
    2039              : {
    2040              :     QofCollection *col;
    2041         4278 :     if (!guid || !book) return nullptr;
    2042         4278 :     col = qof_book_get_collection (book, GNC_ID_ACCOUNT);
    2043         4278 :     return (Account *) qof_collection_lookup_entity (col, guid);
    2044              : }
    2045              : 
    2046              : /********************************************************************\
    2047              : \********************************************************************/
    2048              : 
    2049              : void
    2050            0 : xaccAccountSetMark (Account *acc, short m)
    2051              : {
    2052              :     AccountPrivate *priv;
    2053              : 
    2054            0 :     g_return_if_fail(GNC_IS_ACCOUNT(acc));
    2055              : 
    2056            0 :     priv = GET_PRIVATE(acc);
    2057            0 :     priv->mark = m;
    2058              : }
    2059              : 
    2060              : void
    2061            0 : xaccClearMark (Account *acc, short val)
    2062              : {
    2063              :     Account *root;
    2064              : 
    2065            0 :     g_return_if_fail(GNC_IS_ACCOUNT(acc));
    2066              : 
    2067            0 :     root = gnc_account_get_root(acc);
    2068            0 :     xaccClearMarkDown(root ? root : acc, val);
    2069              : }
    2070              : 
    2071              : void
    2072            0 : xaccClearMarkDown (Account *acc, short val)
    2073              : {
    2074              :     AccountPrivate *priv;
    2075              : 
    2076            0 :     g_return_if_fail(GNC_IS_ACCOUNT(acc));
    2077              : 
    2078            0 :     priv = GET_PRIVATE(acc);
    2079            0 :     priv->mark = val;
    2080            0 :     std::for_each (priv->children.begin(), priv->children.end(),
    2081            0 :                    [val](auto acc){ xaccClearMarkDown(acc, val); });
    2082              : }
    2083              : 
    2084              : /********************************************************************\
    2085              : \********************************************************************/
    2086              : 
    2087              : GNCPolicy *
    2088          356 : gnc_account_get_policy (Account *acc)
    2089              : {
    2090          356 :     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
    2091              : 
    2092          356 :     return GET_PRIVATE(acc)->policy;
    2093              : }
    2094              : 
    2095              : void
    2096            1 : gnc_account_set_policy (Account *acc, GNCPolicy *policy)
    2097              : {
    2098              :     AccountPrivate *priv;
    2099              : 
    2100            1 :     g_return_if_fail(GNC_IS_ACCOUNT(acc));
    2101              : 
    2102            1 :     priv = GET_PRIVATE(acc);
    2103            1 :     priv->policy = policy ? policy : xaccGetFIFOPolicy();
    2104              : }
    2105              : 
    2106              : /********************************************************************\
    2107              : \********************************************************************/
    2108              : 
    2109              : void
    2110          131 : xaccAccountRemoveLot (Account *acc, GNCLot *lot)
    2111              : {
    2112              :     AccountPrivate *priv;
    2113              : 
    2114          131 :     g_return_if_fail(GNC_IS_ACCOUNT(acc));
    2115          131 :     g_return_if_fail(GNC_IS_LOT(lot));
    2116              : 
    2117          131 :     priv = GET_PRIVATE(acc);
    2118          131 :     g_return_if_fail(priv->lots);
    2119              : 
    2120          131 :     ENTER ("(acc=%p, lot=%p)", acc, lot);
    2121          131 :     priv->lots = g_list_remove(priv->lots, lot);
    2122          131 :     qof_event_gen (QOF_INSTANCE(lot), QOF_EVENT_REMOVE, nullptr);
    2123          131 :     qof_event_gen (&acc->inst, QOF_EVENT_MODIFY, nullptr);
    2124          131 :     LEAVE ("(acc=%p, lot=%p)", acc, lot);
    2125              : }
    2126              : 
    2127              : void
    2128          235 : xaccAccountInsertLot (Account *acc, GNCLot *lot)
    2129              : {
    2130              :     /* errors */
    2131          235 :     g_return_if_fail(GNC_IS_ACCOUNT(acc));
    2132          235 :     g_return_if_fail(GNC_IS_LOT(lot));
    2133              : 
    2134              :     /* optimizations */
    2135          235 :     auto lot_acc = gnc_lot_get_account(lot);
    2136          235 :     if (lot_acc == acc)
    2137            1 :         return;
    2138              : 
    2139          234 :     ENTER ("(acc=%p, lot=%p)", acc, lot);
    2140              : 
    2141              :     /* pull it out of the old account */
    2142          234 :     if (lot_acc)
    2143              :     {
    2144            1 :         auto priv = GET_PRIVATE(lot_acc);
    2145            1 :         priv->lots = g_list_remove(priv->lots, lot);
    2146            1 :         qof_event_gen (&lot_acc->inst, QOF_EVENT_MODIFY, nullptr);
    2147              :     }
    2148              : 
    2149          234 :     auto priv = GET_PRIVATE(acc);
    2150          234 :     priv->lots = g_list_prepend(priv->lots, lot);
    2151          234 :     gnc_lot_set_account(lot, acc);
    2152              : 
    2153              :     /* Don't move the splits to the new account.  The caller will do this
    2154              :      * if appropriate, and doing it here will not work if we are being
    2155              :      * called from gnc_book_close_period since xaccAccountInsertSplit
    2156              :      * will try to balance capital gains and things aren't ready for that. */
    2157              : 
    2158          234 :     qof_event_gen (QOF_INSTANCE(lot), QOF_EVENT_ADD, nullptr);
    2159          234 :     qof_event_gen (&acc->inst, QOF_EVENT_MODIFY, nullptr);
    2160              : 
    2161          234 :     LEAVE ("(acc=%p, lot=%p)", acc, lot);
    2162              : }
    2163              : 
    2164              : /********************************************************************\
    2165              : \********************************************************************/
    2166              : static void
    2167            0 : xaccPreSplitMove (Split *split)
    2168              : {
    2169            0 :     xaccTransBeginEdit (xaccSplitGetParent (split));
    2170            0 : }
    2171              : 
    2172              : static void
    2173            0 : xaccPostSplitMove (Split *split, Account *accto)
    2174              : {
    2175              :     Transaction *trans;
    2176              : 
    2177            0 :     xaccSplitSetAccount(split, accto);
    2178            0 :     xaccSplitSetAmount(split, split->amount);
    2179            0 :     trans = xaccSplitGetParent (split);
    2180            0 :     xaccTransCommitEdit (trans);
    2181            0 : }
    2182              : 
    2183              : void
    2184            0 : xaccAccountMoveAllSplits (Account *accfrom, Account *accto)
    2185              : {
    2186              :     AccountPrivate *from_priv;
    2187              : 
    2188              :     /* errors */
    2189            0 :     g_return_if_fail(GNC_IS_ACCOUNT(accfrom));
    2190            0 :     g_return_if_fail(GNC_IS_ACCOUNT(accto));
    2191              : 
    2192              :     /* optimizations */
    2193            0 :     from_priv = GET_PRIVATE(accfrom);
    2194            0 :     if (from_priv->splits.empty() || accfrom == accto)
    2195            0 :         return;
    2196              : 
    2197              :     /* check for book mix-up */
    2198            0 :     g_return_if_fail (qof_instance_books_equal(accfrom, accto));
    2199            0 :     ENTER ("(accfrom=%p, accto=%p)", accfrom, accto);
    2200              : 
    2201            0 :     xaccAccountBeginEdit(accfrom);
    2202            0 :     xaccAccountBeginEdit(accto);
    2203              :     /* Begin editing both accounts and all transactions in accfrom. */
    2204            0 :     std::for_each (from_priv->splits.begin(), from_priv->splits.end(), xaccPreSplitMove);
    2205              : 
    2206              :     /* Concatenate accfrom's lists of splits and lots to accto's lists. */
    2207              :     //to_priv->splits = g_list_concat(to_priv->splits, from_priv->splits);
    2208              :     //to_priv->lots = g_list_concat(to_priv->lots, from_priv->lots);
    2209              : 
    2210              :     /* Set appropriate flags. */
    2211              :     //from_priv->balance_dirty = TRUE;
    2212              :     //from_priv->sort_dirty = FALSE;
    2213              :     //to_priv->balance_dirty = TRUE;
    2214              :     //to_priv->sort_dirty = TRUE;
    2215              : 
    2216              :     /*
    2217              :      * Change each split's account back pointer to accto.
    2218              :      * Convert each split's amount to accto's commodity.
    2219              :      * Commit to editing each transaction.
    2220              :      */
    2221            0 :     auto splits = from_priv->splits;
    2222            0 :     std::for_each (splits.begin(), splits.end(), [accto](auto s){ xaccPostSplitMove (s, accto); });
    2223              : 
    2224              :     /* Finally empty accfrom. */
    2225            0 :     g_assert(from_priv->splits.empty());
    2226            0 :     g_assert(from_priv->lots == nullptr);
    2227            0 :     xaccAccountCommitEdit(accfrom);
    2228            0 :     xaccAccountCommitEdit(accto);
    2229              : 
    2230            0 :     LEAVE ("(accfrom=%p, accto=%p)", accfrom, accto);
    2231            0 : }
    2232              : 
    2233              : 
    2234              : /********************************************************************\
    2235              :  * xaccAccountRecomputeBalance                                      *
    2236              :  *   recomputes the partial balances and the current balance for    *
    2237              :  *   this account.                                                  *
    2238              :  *                                                                  *
    2239              :  * The way the computation is done depends on whether the partial   *
    2240              :  * balances are for a monetary account (bank, cash, etc.) or a      *
    2241              :  * certificate account (stock portfolio, mutual fund).  For bank    *
    2242              :  * accounts, the invariant amount is the dollar amount. For share   *
    2243              :  * accounts, the invariant amount is the number of shares. For      *
    2244              :  * share accounts, the share price fluctuates, and the current      *
    2245              :  * value of such an account is the number of shares times the       *
    2246              :  * current share price.                                             *
    2247              :  *                                                                  *
    2248              :  * Part of the complexity of this computation stems from the fact   *
    2249              :  * xacc uses a double-entry system, meaning that one transaction    *
    2250              :  * appears in two accounts: one account is debited, and the other   *
    2251              :  * is credited.  When the transaction represents a sale of shares,  *
    2252              :  * or a purchase of shares, some care must be taken to compute      *
    2253              :  * balances correctly.  For a sale of shares, the stock account must*
    2254              :  * be debited in shares, but the bank account must be credited      *
    2255              :  * in dollars.  Thus, two different mechanisms must be used to      *
    2256              :  * compute balances, depending on account type.                     *
    2257              :  *                                                                  *
    2258              :  * Args:   account -- the account for which to recompute balances   *
    2259              :  * Return: void                                                     *
    2260              : \********************************************************************/
    2261              : 
    2262              : void
    2263        34941 : xaccAccountRecomputeBalance (Account * acc)
    2264              : {
    2265        56978 :     if (nullptr == acc) return;
    2266              : 
    2267        34118 :     auto priv = GET_PRIVATE(acc);
    2268        34118 :     if (qof_instance_get_editlevel(acc) > 0) return;
    2269        31735 :     if (!priv->balance_dirty || priv->defer_bal_computation) return;
    2270        21475 :     if (qof_instance_get_destroying(acc)) return;
    2271        21475 :     if (qof_book_shutting_down(qof_instance_get_book(acc))) return;
    2272              : 
    2273        12081 :     auto balance            = priv->starting_balance;
    2274        12081 :     auto noclosing_balance  = priv->starting_noclosing_balance;
    2275        12081 :     auto cleared_balance    = priv->starting_cleared_balance;
    2276        12081 :     auto reconciled_balance = priv->starting_reconciled_balance;
    2277        12081 :     auto has_stock_split    = false;
    2278              : 
    2279        12081 :     PINFO ("acct=%s starting baln=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT,
    2280              :            priv->accountName, balance.num, balance.denom);
    2281       121228 :     for (auto split : priv->splits)
    2282              :     {
    2283        97066 :         auto amt = xaccSplitGetAmount (split);
    2284              : 
    2285        97066 :         if (xaccSplitIsStockSplit(split))
    2286              :         {
    2287           97 :             if (gnc_numeric_zero_p(balance))
    2288            1 :                 continue;
    2289           96 :             auto new_balance = gnc_numeric_add_fixed(balance, amt);
    2290           96 :             if (gnc_numeric_zero_p(new_balance))
    2291            0 :                 continue;
    2292           96 :             auto ratio = gnc_numeric_div(new_balance, balance, GNC_DENOM_AUTO,
    2293              :                                          GNC_HOW_DENOM_REDUCE);
    2294           96 :             auto denom = xaccAccountGetCommoditySCU(acc);
    2295         1403 :             for (auto psplit : priv->splits)
    2296              :             {
    2297         1307 :                 if (psplit == split)
    2298           96 :                     break;
    2299         1211 :                 xaccSplitSetAdjustedAmount(psplit,
    2300              :                                            gnc_numeric_mul(xaccSplitGetAdjustedAmount(psplit), ratio, denom,
    2301              :                                                    GNC_HOW_RND_ROUND_HALF_UP));
    2302              :             }
    2303           96 :             balance = new_balance;
    2304           96 :             has_stock_split = true;
    2305              :         }
    2306              :         else
    2307              :         {
    2308        96969 :             split->adjusted_amount = amt;
    2309        96969 :             balance = gnc_numeric_add_fixed(balance, amt);
    2310              :         }
    2311              : 
    2312        97065 :         if (NREC != split->reconciled)
    2313              :         {
    2314         9959 :             cleared_balance = gnc_numeric_add_fixed(cleared_balance, amt);
    2315              :         }
    2316              : 
    2317        97065 :         if (YREC == split->reconciled ||
    2318        95132 :                 FREC == split->reconciled)
    2319              :         {
    2320              :             reconciled_balance =
    2321         3724 :                 gnc_numeric_add_fixed(reconciled_balance, amt);
    2322              :         }
    2323              : 
    2324        97065 :         if (!(xaccTransGetIsClosingTxn (split->parent)))
    2325        96740 :             noclosing_balance = gnc_numeric_add_fixed(noclosing_balance, amt);
    2326              : 
    2327        97065 :         split->balance = balance;
    2328        97065 :         split->noclosing_balance = noclosing_balance;
    2329        97065 :         split->cleared_balance = cleared_balance;
    2330        97065 :         split->reconciled_balance = reconciled_balance;
    2331              : 
    2332              :     }
    2333              : 
    2334        12081 :     priv->balance = balance;
    2335        12081 :     priv->noclosing_balance = noclosing_balance;
    2336        12081 :     priv->cleared_balance = cleared_balance;
    2337        12081 :     priv->reconciled_balance = reconciled_balance;
    2338        12081 :     priv->balance_dirty = FALSE;
    2339        12081 :     priv->has_stock_split = has_stock_split;
    2340              : }
    2341              : 
    2342              : /********************************************************************\
    2343              : \********************************************************************/
    2344              : 
    2345              : /* The sort order is used to implicitly define an
    2346              :  * order for report generation */
    2347              : 
    2348              : static int typeorder[NUM_ACCOUNT_TYPES] =
    2349              : {
    2350              :     ACCT_TYPE_BANK, ACCT_TYPE_STOCK, ACCT_TYPE_MUTUAL, ACCT_TYPE_CURRENCY,
    2351              :     ACCT_TYPE_CASH, ACCT_TYPE_ASSET, ACCT_TYPE_RECEIVABLE,
    2352              :     ACCT_TYPE_CREDIT, ACCT_TYPE_LIABILITY, ACCT_TYPE_PAYABLE,
    2353              :     ACCT_TYPE_INCOME, ACCT_TYPE_EXPENSE, ACCT_TYPE_EQUITY, ACCT_TYPE_TRADING
    2354              : };
    2355              : 
    2356              : static int revorder[NUM_ACCOUNT_TYPES] =
    2357              : {
    2358              :     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
    2359              : };
    2360              : 
    2361              : 
    2362              : int
    2363        14502 : xaccAccountOrder (const Account *aa, const Account *ab)
    2364              : {
    2365              :     AccountPrivate *priv_aa, *priv_ab;
    2366              :     const char *da, *db;
    2367              :     int ta, tb, result;
    2368              : 
    2369        14502 :     if (aa == ab) return 0;
    2370        14500 :     if (!ab) return -1;
    2371        14499 :     if (!aa) return +1;
    2372              : 
    2373        14498 :     priv_aa = GET_PRIVATE(aa);
    2374        14498 :     priv_ab = GET_PRIVATE(ab);
    2375              : 
    2376              :     /* sort on accountCode strings */
    2377        14498 :     da = priv_aa->accountCode;
    2378        14498 :     db = priv_ab->accountCode;
    2379              : 
    2380              :     /* Otherwise do a string sort */
    2381        14498 :     result = g_strcmp0 (da, db);
    2382        14498 :     if (result)
    2383           41 :         return result;
    2384              : 
    2385              :     /* if account-type-order array not initialized, initialize it */
    2386              :     /* this will happen at most once during program invocation */
    2387        14457 :     if (-1 == revorder[0])
    2388              :     {
    2389              :         int i;
    2390          400 :         for (i = 0; i < NUM_ACCOUNT_TYPES; i++)
    2391              :         {
    2392          375 :             revorder [typeorder[i]] = i;
    2393              :         }
    2394              :     }
    2395              : 
    2396              :     /* otherwise, sort on account type */
    2397        14457 :     ta = priv_aa->type;
    2398        14457 :     tb = priv_ab->type;
    2399        14457 :     ta = revorder[ta];
    2400        14457 :     tb = revorder[tb];
    2401        14457 :     if (ta < tb) return -1;
    2402        13236 :     if (ta > tb) return +1;
    2403              : 
    2404              :     /* otherwise, sort on accountName strings */
    2405         8054 :     da = priv_aa->accountName;
    2406         8054 :     db = priv_ab->accountName;
    2407         8054 :     result = safe_utf8_collate(da, db);
    2408         8054 :     if (result)
    2409         7944 :         return result;
    2410              : 
    2411              :     /* guarantee a stable sort */
    2412          110 :     return qof_instance_guid_compare(aa, ab);
    2413              : }
    2414              : 
    2415              : static int
    2416            0 : qof_xaccAccountOrder (const Account **aa, const Account **ab)
    2417              : {
    2418            0 :     return xaccAccountOrder(*aa, *ab);
    2419              : }
    2420              : 
    2421              : /********************************************************************\
    2422              : \********************************************************************/
    2423              : 
    2424              : void
    2425         4381 : xaccAccountSetType (Account *acc, GNCAccountType tip)
    2426              : {
    2427              :     AccountPrivate *priv;
    2428              : 
    2429              :     /* errors */
    2430         4381 :     g_return_if_fail(GNC_IS_ACCOUNT(acc));
    2431         4381 :     g_return_if_fail(tip < NUM_ACCOUNT_TYPES);
    2432              : 
    2433              :     /* optimizations */
    2434         4381 :     priv = GET_PRIVATE(acc);
    2435         4381 :     if (priv->type == tip)
    2436           64 :         return;
    2437              : 
    2438         4317 :     xaccAccountBeginEdit(acc);
    2439         4317 :     priv->type = tip;
    2440         4317 :     priv->balance_dirty = TRUE; /* new type may affect balance computation */
    2441         4317 :     mark_account(acc);
    2442         4317 :     xaccAccountCommitEdit(acc);
    2443              : }
    2444              : 
    2445              : void
    2446         5412 : xaccAccountSetName (Account *acc, const char *str)
    2447              : {
    2448              :     AccountPrivate *priv;
    2449              : 
    2450              :     /* errors */
    2451         5412 :     g_return_if_fail(GNC_IS_ACCOUNT(acc));
    2452         5412 :     g_return_if_fail(str);
    2453              : 
    2454              :     /* optimizations */
    2455         5412 :     priv = GET_PRIVATE(acc);
    2456         5412 :     if (g_strcmp0(str, priv->accountName) == 0)
    2457           14 :         return;
    2458              : 
    2459         5398 :     xaccAccountBeginEdit(acc);
    2460         5398 :     priv->accountName = qof_string_cache_replace(priv->accountName, str);
    2461         5398 :     mark_account (acc);
    2462         5398 :     xaccAccountCommitEdit(acc);
    2463              : }
    2464              : 
    2465              : void
    2466         1244 : xaccAccountSetCode (Account *acc, const char *str)
    2467              : {
    2468              :     AccountPrivate *priv;
    2469              : 
    2470              :     /* errors */
    2471         1244 :     g_return_if_fail(GNC_IS_ACCOUNT(acc));
    2472              : 
    2473              :     /* optimizations */
    2474         1244 :     priv = GET_PRIVATE(acc);
    2475         1244 :     if (g_strcmp0(str, priv->accountCode) == 0)
    2476           89 :         return;
    2477              : 
    2478         1155 :     xaccAccountBeginEdit(acc);
    2479         1155 :     priv->accountCode = qof_string_cache_replace(priv->accountCode, str ? str : "");
    2480         1155 :     mark_account (acc);
    2481         1155 :     xaccAccountCommitEdit(acc);
    2482              : }
    2483              : 
    2484              : void
    2485         2165 : xaccAccountSetDescription (Account *acc, const char *str)
    2486              : {
    2487              :     AccountPrivate *priv;
    2488              : 
    2489              :     /* errors */
    2490         2165 :     g_return_if_fail(GNC_IS_ACCOUNT(acc));
    2491              : 
    2492              :     /* optimizations */
    2493         2165 :     priv = GET_PRIVATE(acc);
    2494         2165 :     if (g_strcmp0(str, priv->description) == 0)
    2495          638 :         return;
    2496              : 
    2497         1527 :     xaccAccountBeginEdit(acc);
    2498         1527 :     priv->description = qof_string_cache_replace(priv->description, str ? str : "");
    2499         1527 :     mark_account (acc);
    2500         1527 :     xaccAccountCommitEdit(acc);
    2501              : }
    2502              : 
    2503              : static void
    2504            6 : set_kvp_gnc_numeric_path (Account *acc, const std::vector<std::string>& path,
    2505              :                           std::optional<gnc_numeric> value)
    2506              : {
    2507            6 :     xaccAccountBeginEdit(acc);
    2508            6 :     qof_instance_set_path_kvp<gnc_numeric> (QOF_INSTANCE(acc), value, path);
    2509            6 :     xaccAccountCommitEdit(acc);
    2510            6 : }
    2511              : 
    2512              : static std::optional<gnc_numeric>
    2513           11 : get_kvp_gnc_numeric_path (const Account *acc, const Path& path)
    2514              : {
    2515           11 :     return qof_instance_get_path_kvp<gnc_numeric> (QOF_INSTANCE(acc), path);
    2516              : }
    2517              : 
    2518              : static void
    2519         1826 : set_kvp_string_path (Account *acc, std::vector<std::string> const & path,
    2520              :                      const char *value)
    2521              : {
    2522         1826 :     std::optional<const char*> val;
    2523         1826 :     if (value && *value)
    2524           37 :         val = g_strdup(value);
    2525              : 
    2526         1826 :     xaccAccountBeginEdit(acc);
    2527         1826 :     qof_instance_set_path_kvp<const char*> (QOF_INSTANCE(acc), val, path);
    2528         1826 :     xaccAccountCommitEdit(acc);
    2529         1826 : }
    2530              : 
    2531              : static const char*
    2532         2858 : get_kvp_string_path (const Account *acc, const Path& path)
    2533              : {
    2534         2858 :     auto rv{qof_instance_get_path_kvp<const char*> (QOF_INSTANCE(acc), path)};
    2535         5716 :     return rv ? *rv : nullptr;
    2536              : }
    2537              : 
    2538              : static void
    2539           81 : set_kvp_account_path (Account* acc, const Path& path, const Account* kvp_account)
    2540              : {
    2541           81 :     std::optional<GncGUID*> val;
    2542           81 :     if (kvp_account)
    2543           80 :         val = guid_copy(xaccAccountGetGUID (kvp_account));
    2544              : 
    2545           81 :     xaccAccountBeginEdit(acc);
    2546           81 :     qof_instance_set_path_kvp<GncGUID*> (QOF_INSTANCE(acc), val, path);
    2547           81 :     xaccAccountCommitEdit(acc);
    2548           81 : }
    2549              : 
    2550              : static Account*
    2551           15 : get_kvp_account_path (const Account *acc, const Path& path)
    2552              : {
    2553           15 :     auto val{qof_instance_get_path_kvp<GncGUID*> (QOF_INSTANCE(acc), path)};
    2554           30 :     return val ? xaccAccountLookup (*val, gnc_account_get_book (acc)) : nullptr;
    2555              : }
    2556              : 
    2557              : static void
    2558          177 : set_kvp_boolean_path (Account *acc, const Path& path, gboolean option)
    2559              : {
    2560          177 :     set_kvp_string_path (acc, path, option ? "true" : nullptr);
    2561          177 : }
    2562              : 
    2563              : static gboolean
    2564          261 : get_kvp_boolean_path (const Account *acc, const Path& path)
    2565              : {
    2566          261 :     auto slot{QOF_INSTANCE(acc)->kvp_data->get_slot(path)};
    2567          261 :     if (!slot) return false;
    2568           10 :     switch (slot->get_type())
    2569              :     {
    2570            1 :     case KvpValueImpl::Type::INT64:
    2571            1 :         return slot->get<int64_t>() != 0;
    2572            9 :     case KvpValueImpl::Type::STRING:
    2573            9 :         return g_strcmp0 (slot->get<const char*>(), "true") == 0;
    2574            0 :     default:
    2575            0 :         return false;
    2576              :     }
    2577              : }
    2578              : 
    2579              : static void
    2580           19 : set_kvp_int64_path (Account *acc, const Path& path, std::optional<gint64> value)
    2581              : {
    2582           19 :     xaccAccountBeginEdit(acc);
    2583           19 :     qof_instance_set_path_kvp<int64_t> (QOF_INSTANCE(acc), value, path);
    2584           19 :     xaccAccountCommitEdit(acc);
    2585           19 : }
    2586              : 
    2587              : static const std::optional<gint64>
    2588           21 : get_kvp_int64_path (const Account *acc, const Path& path)
    2589              : {
    2590           21 :     return qof_instance_get_path_kvp<int64_t> (QOF_INSTANCE(acc), path);
    2591              : }
    2592              : 
    2593              : static GncGUID*
    2594            2 : get_kvp_guid_path (const Account *acc, const Path& path)
    2595              : {
    2596            2 :     auto val{qof_instance_get_path_kvp<GncGUID*> (QOF_INSTANCE(acc), path)};
    2597            4 :     return val ? guid_copy(*val) : nullptr;
    2598              : }
    2599              : 
    2600              : void
    2601          707 : xaccAccountSetColor (Account *acc, const char *str)
    2602              : {
    2603         1414 :     set_kvp_string_path (acc, {"color"}, str);
    2604          707 : }
    2605              : 
    2606              : void
    2607            5 : xaccAccountSetFilter (Account *acc, const char *str)
    2608              : {
    2609           10 :     set_kvp_string_path (acc, {"filter"}, str);
    2610            5 : }
    2611              : 
    2612              : void
    2613            5 : xaccAccountSetSortOrder (Account *acc, const char *str)
    2614              : {
    2615           10 :     set_kvp_string_path (acc, {"sort-order"}, str);
    2616            5 : }
    2617              : 
    2618              : void
    2619            2 : xaccAccountSetSortReversed (Account *acc, gboolean sortreversed)
    2620              : {
    2621            4 :     set_kvp_boolean_path (acc, {"sort-reversed"}, sortreversed);
    2622            2 : }
    2623              : 
    2624              : static void
    2625            1 : qofAccountSetParent (Account *acc, QofInstance *parent)
    2626              : {
    2627              :     Account *parent_acc;
    2628              : 
    2629            1 :     g_return_if_fail(GNC_IS_ACCOUNT(acc));
    2630            1 :     g_return_if_fail(GNC_IS_ACCOUNT(parent));
    2631              : 
    2632            1 :     parent_acc = GNC_ACCOUNT(parent);
    2633            1 :     xaccAccountBeginEdit(acc);
    2634            1 :     xaccAccountBeginEdit(parent_acc);
    2635            1 :     gnc_account_append_child(parent_acc, acc);
    2636            1 :     mark_account (parent_acc);
    2637            1 :     mark_account (acc);
    2638            1 :     xaccAccountCommitEdit(acc);
    2639            1 :     xaccAccountCommitEdit(parent_acc);
    2640              : }
    2641              : 
    2642              : void
    2643          521 : xaccAccountSetNotes (Account *acc, const char *str)
    2644              : {
    2645         1042 :     set_kvp_string_path (acc, {"notes"}, str);
    2646          521 : }
    2647              : 
    2648              : void
    2649            4 : xaccAccountSetOnlineID (Account *acc, const char *id)
    2650              : {
    2651            4 :     g_return_if_fail (GNC_IS_ACCOUNT(acc));
    2652           12 :     set_kvp_string_path (acc, {KEY_ONLINE_ID}, id);
    2653            4 : }
    2654              : 
    2655              : 
    2656              : void
    2657           71 : xaccAccountSetAssociatedAccount (Account *acc, const char *tag, const Account* assoc_acct)
    2658              : {
    2659           71 :     g_return_if_fail (GNC_IS_ACCOUNT(acc));
    2660           70 :     g_return_if_fail (tag && *tag);
    2661              : 
    2662          345 :     set_kvp_account_path (acc, {"associated-account", tag}, assoc_acct);
    2663          138 : }
    2664              : 
    2665              : void
    2666         3409 : xaccAccountSetCommodity (Account * acc, gnc_commodity * com)
    2667              : {
    2668              :     AccountPrivate *priv;
    2669              : 
    2670              :     /* errors */
    2671         3409 :     g_return_if_fail(GNC_IS_ACCOUNT(acc));
    2672         3409 :     g_return_if_fail(GNC_IS_COMMODITY(com));
    2673              : 
    2674              :     /* optimizations */
    2675         3214 :     priv = GET_PRIVATE(acc);
    2676         3214 :     if (com == priv->commodity)
    2677           47 :         return;
    2678              : 
    2679         3167 :     xaccAccountBeginEdit(acc);
    2680         3167 :     gnc_commodity_decrement_usage_count(priv->commodity);
    2681         3167 :     priv->commodity = com;
    2682         3167 :     gnc_commodity_increment_usage_count(com);
    2683         3167 :     priv->commodity_scu = gnc_commodity_get_fraction(com);
    2684         3167 :     priv->non_standard_scu = FALSE;
    2685              : 
    2686              :     /* iterate over splits */
    2687         6343 :     for (auto s : priv->splits)
    2688              :     {
    2689            9 :         Transaction *trans = xaccSplitGetParent (s);
    2690              : 
    2691            9 :         xaccTransBeginEdit (trans);
    2692            9 :         xaccSplitSetAmount (s, xaccSplitGetAmount(s));
    2693            9 :         xaccTransCommitEdit (trans);
    2694              :     }
    2695              : 
    2696         3167 :     priv->sort_dirty = TRUE;  /* Not needed. */
    2697         3167 :     priv->balance_dirty = TRUE;
    2698         3167 :     mark_account (acc);
    2699              : 
    2700         3167 :     xaccAccountCommitEdit(acc);
    2701              : }
    2702              : 
    2703              : /*
    2704              :  * Set the account scu and then check to see if it is the same as the
    2705              :  * commodity scu.  This function is called when parsing the data file
    2706              :  * and is designed to catch cases where the two were accidentally set
    2707              :  * to mismatched values in the past.
    2708              :  */
    2709              : void
    2710          905 : xaccAccountSetCommoditySCU (Account *acc, int scu)
    2711              : {
    2712              :     AccountPrivate *priv;
    2713              : 
    2714          905 :     g_return_if_fail(GNC_IS_ACCOUNT(acc));
    2715              : 
    2716          905 :     priv = GET_PRIVATE(acc);
    2717          905 :     xaccAccountBeginEdit(acc);
    2718          905 :     priv->commodity_scu = scu;
    2719          905 :     if (scu != gnc_commodity_get_fraction(priv->commodity))
    2720          100 :         priv->non_standard_scu = TRUE;
    2721          905 :     mark_account(acc);
    2722          905 :     xaccAccountCommitEdit(acc);
    2723              : }
    2724              : 
    2725              : int
    2726          706 : xaccAccountGetCommoditySCUi (const Account * acc)
    2727              : {
    2728          706 :     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), 0);
    2729          706 :     return GET_PRIVATE(acc)->commodity_scu;
    2730              : }
    2731              : 
    2732              : int
    2733        18300 : xaccAccountGetCommoditySCU (const Account * acc)
    2734              : {
    2735              :     AccountPrivate *priv;
    2736              : 
    2737        18300 :     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), 0);
    2738              : 
    2739        18300 :     priv = GET_PRIVATE(acc);
    2740        18300 :     if (priv->non_standard_scu || !priv->commodity)
    2741         1805 :         return priv->commodity_scu;
    2742        16495 :     return gnc_commodity_get_fraction(priv->commodity);
    2743              : }
    2744              : 
    2745              : void
    2746           46 : xaccAccountSetNonStdSCU (Account *acc, gboolean flag)
    2747              : {
    2748              :     AccountPrivate *priv;
    2749              : 
    2750           46 :     g_return_if_fail(GNC_IS_ACCOUNT(acc));
    2751              : 
    2752           46 :     priv = GET_PRIVATE(acc);
    2753           46 :     if (priv->non_standard_scu == flag)
    2754           42 :         return;
    2755            4 :     xaccAccountBeginEdit(acc);
    2756            4 :     priv->non_standard_scu = flag;
    2757            4 :     mark_account (acc);
    2758            4 :     xaccAccountCommitEdit(acc);
    2759              : }
    2760              : 
    2761              : gboolean
    2762          251 : xaccAccountGetNonStdSCU (const Account * acc)
    2763              : {
    2764          251 :     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), 0);
    2765          251 :     return GET_PRIVATE(acc)->non_standard_scu;
    2766              : }
    2767              : 
    2768              : /********************************************************************\
    2769              : \********************************************************************/
    2770              : /* below follow the old, deprecated currency/security routines. */
    2771              : 
    2772              : void
    2773            0 : DxaccAccountSetCurrency (Account * acc, gnc_commodity * currency)
    2774              : {
    2775            0 :     if ((!acc) || (!currency)) return;
    2776              : 
    2777            0 :     auto s = gnc_commodity_get_unique_name (currency);
    2778            0 :     set_kvp_string_path (acc, {"old-currency"}, s);
    2779              : 
    2780            0 :     auto book = qof_instance_get_book(acc);
    2781            0 :     auto table = gnc_commodity_table_get_table (book);
    2782            0 :     auto commodity = gnc_commodity_table_lookup_unique (table, s);
    2783              : 
    2784            0 :     if (!commodity)
    2785            0 :         gnc_commodity_table_insert (table, currency);
    2786              : }
    2787              : 
    2788              : /********************************************************************\
    2789              : \********************************************************************/
    2790              : 
    2791              : void
    2792        18211 : gnc_account_foreach_descendant (const Account *acc, std::function<void(Account*)> account_cb)
    2793              : {
    2794        18211 :     g_return_if_fail (GNC_IS_ACCOUNT(acc));
    2795              : 
    2796              :     // children must be a vector copy instead of reference because
    2797              :     // some callers e.g. xaccAccountTreeScrubLots will modify the
    2798              :     // children
    2799        18211 :     auto children = GET_PRIVATE(acc)->children;
    2800        53065 :     for (auto child : children)
    2801              :     {
    2802        16643 :         account_cb (child);
    2803        16643 :         gnc_account_foreach_descendant (child, account_cb);
    2804              :     }
    2805        18211 : }
    2806              : 
    2807              : static void
    2808        13149 : account_foreach_descendant_sorted (const Account *acc, std::function<void(Account*)> account_cb)
    2809              : {
    2810        13149 :     g_return_if_fail (GNC_IS_ACCOUNT(acc));
    2811              : 
    2812        13149 :     auto children = GET_PRIVATE(acc)->children;
    2813        13149 :     std::sort (children.begin(), children.end(),
    2814        13340 :                [](auto a, auto b) { return xaccAccountOrder (a, b) < 0; });
    2815              : 
    2816        37068 :     for (auto child : children)
    2817              :     {
    2818        10770 :         account_cb (child);
    2819        10770 :         account_foreach_descendant_sorted (child, account_cb);
    2820              :     }
    2821        13149 : }
    2822              : 
    2823              : void
    2824         4355 : gnc_account_append_child (Account *new_parent, Account *child)
    2825              : {
    2826              :     AccountPrivate *ppriv, *cpriv;
    2827              :     Account *old_parent;
    2828              :     QofCollection *col;
    2829              : 
    2830              :     /* errors */
    2831         4355 :     g_assert(GNC_IS_ACCOUNT(new_parent));
    2832         4355 :     g_assert(GNC_IS_ACCOUNT(child));
    2833              : 
    2834              :     /* optimizations */
    2835         4355 :     ppriv = GET_PRIVATE(new_parent);
    2836         4355 :     cpriv = GET_PRIVATE(child);
    2837         4355 :     old_parent = cpriv->parent;
    2838         4355 :     if (old_parent == new_parent)
    2839          385 :         return;
    2840              : 
    2841              :     //  xaccAccountBeginEdit(new_parent);
    2842         3970 :     xaccAccountBeginEdit(child);
    2843         3970 :     if (old_parent)
    2844              :     {
    2845           16 :         gnc_account_remove_child(old_parent, child);
    2846              : 
    2847           16 :         if (!qof_instance_books_equal(old_parent, new_parent))
    2848              :         {
    2849              :             /* hack alert -- this implementation is not exactly correct.
    2850              :              * If the entity tables are not identical, then the 'from' book
    2851              :              * may have a different backend than the 'to' book.  This means
    2852              :              * that we should get the 'from' backend to destroy this account,
    2853              :              * and the 'to' backend to save it.  Right now, this is broken.
    2854              :              *
    2855              :              * A 'correct' implementation similar to this is in Period.c
    2856              :              * except its for transactions ...
    2857              :              *
    2858              :              * Note also, we need to reparent the children to the new book as well.
    2859              :              */
    2860            1 :             PWARN ("reparenting accounts across books is not correctly supported\n");
    2861              : 
    2862            1 :             qof_event_gen (&child->inst, QOF_EVENT_DESTROY, nullptr);
    2863            1 :             col = qof_book_get_collection (qof_instance_get_book(new_parent),
    2864              :                                            GNC_ID_ACCOUNT);
    2865            1 :             qof_collection_insert_entity (col, &child->inst);
    2866            1 :             qof_event_gen (&child->inst, QOF_EVENT_CREATE, nullptr);
    2867              :         }
    2868              :     }
    2869         3970 :     cpriv->parent = new_parent;
    2870         3970 :     ppriv->children.push_back (child);
    2871         3970 :     qof_instance_set_dirty(&new_parent->inst);
    2872         3970 :     qof_instance_set_dirty(&child->inst);
    2873              : 
    2874              :     /* Send events data. Warning: The call to commit_edit is also going
    2875              :      * to send a MODIFY event. If the gtktreemodelfilter code gets the
    2876              :      * MODIFY before it gets the ADD, it gets very confused and thinks
    2877              :      * that two nodes have been added. */
    2878         3970 :     qof_event_gen (&child->inst, QOF_EVENT_ADD, nullptr);
    2879              :     // qof_event_gen (&new_parent->inst, QOF_EVENT_MODIFY, nullptr);
    2880              : 
    2881         3970 :     xaccAccountCommitEdit (child);
    2882              :     //  xaccAccountCommitEdit(new_parent);
    2883              : }
    2884              : 
    2885              : void
    2886         1851 : gnc_account_remove_child (Account *parent, Account *child)
    2887              : {
    2888              :     AccountPrivate *ppriv, *cpriv;
    2889              :     GncEventData ed;
    2890              : 
    2891         1852 :     if (!child) return;
    2892              : 
    2893              :     /* Note this routine might be called on accounts which
    2894              :      * are not yet parented. */
    2895         1851 :     if (!parent) return;
    2896              : 
    2897         1851 :     ppriv = GET_PRIVATE(parent);
    2898         1851 :     cpriv = GET_PRIVATE(child);
    2899              : 
    2900         1851 :     if (cpriv->parent != parent)
    2901              :     {
    2902            1 :         PERR ("account not a child of parent");
    2903            1 :         return;
    2904              :     }
    2905              : 
    2906              :     /* Gather event data */
    2907         1850 :     ed.node = parent;
    2908         1850 :     ed.idx = gnc_account_child_index (parent, child);
    2909              : 
    2910         3700 :     ppriv->children.erase (std::remove (ppriv->children.begin(), ppriv->children.end(), child),
    2911         1850 :                            ppriv->children.end());
    2912              : 
    2913              :     /* Now send the event. */
    2914         1850 :     qof_event_gen(&child->inst, QOF_EVENT_REMOVE, &ed);
    2915              : 
    2916              :     /* clear the account's parent pointer after REMOVE event generation. */
    2917         1850 :     cpriv->parent = nullptr;
    2918              : 
    2919         1850 :     qof_event_gen (&parent->inst, QOF_EVENT_MODIFY, nullptr);
    2920              : }
    2921              : 
    2922              : Account *
    2923        36967 : gnc_account_get_parent (const Account *acc)
    2924              : {
    2925        36967 :     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
    2926        36967 :     return GET_PRIVATE(acc)->parent;
    2927              : }
    2928              : 
    2929              : Account *
    2930         1603 : gnc_account_get_root (Account *acc)
    2931              : {
    2932         1603 :     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
    2933              : 
    2934         4093 :     while (auto parent = gnc_account_get_parent (acc))
    2935         2490 :         acc = parent;
    2936              : 
    2937         1603 :     return acc;
    2938              : }
    2939              : 
    2940              : gboolean
    2941        39029 : gnc_account_is_root (const Account *account)
    2942              : {
    2943        39029 :     g_return_val_if_fail(GNC_IS_ACCOUNT(account), FALSE);
    2944        39029 :     return (GET_PRIVATE(account)->parent == nullptr);
    2945              : }
    2946              : 
    2947              : GList *
    2948         3022 : gnc_account_get_children (const Account *account)
    2949              : {
    2950         3022 :     g_return_val_if_fail(GNC_IS_ACCOUNT(account), nullptr);
    2951         3022 :     auto& children = GET_PRIVATE(account)->children;
    2952         6044 :     return std::accumulate (children.rbegin(), children.rend(), static_cast<GList*>(nullptr),
    2953         3022 :                             g_list_prepend);
    2954              : }
    2955              : 
    2956              : GList *
    2957         1442 : gnc_account_get_children_sorted (const Account *account)
    2958              : {
    2959         1442 :     g_return_val_if_fail(GNC_IS_ACCOUNT(account), nullptr);
    2960         1442 :     return g_list_sort(gnc_account_get_children (account), (GCompareFunc)xaccAccountOrder);
    2961              : }
    2962              : 
    2963              : gint
    2964            0 : gnc_account_n_children (const Account *account)
    2965              : {
    2966            0 :     g_return_val_if_fail(GNC_IS_ACCOUNT(account), 0);
    2967            0 :     return GET_PRIVATE(account)->children.size();
    2968              : }
    2969              : 
    2970              : gint
    2971         1853 : gnc_account_child_index (const Account *parent, const Account *child)
    2972              : {
    2973         1853 :     g_return_val_if_fail(GNC_IS_ACCOUNT(parent), -1);
    2974         1853 :     g_return_val_if_fail(GNC_IS_ACCOUNT(child), -1);
    2975         1853 :     auto& children = GET_PRIVATE(parent)->children;
    2976         1853 :     auto find_it = std::find (children.begin(), children.end(), child);
    2977         5557 :     return find_it == children.end() ? -1 : std::distance (children.begin(), find_it);
    2978              : }
    2979              : 
    2980              : Account *
    2981            0 : gnc_account_nth_child (const Account *parent, gint num)
    2982              : {
    2983            0 :     g_return_val_if_fail(GNC_IS_ACCOUNT(parent), nullptr);
    2984            0 :     if ((size_t)num >= GET_PRIVATE(parent)->children.size())
    2985            0 :         return nullptr;
    2986            0 :     return static_cast<Account*>(GET_PRIVATE(parent)->children.at (num));
    2987              : }
    2988              : 
    2989              : gint
    2990           26 : gnc_account_n_descendants (const Account *account)
    2991              : {
    2992           26 :     int count {0};
    2993           26 :     gnc_account_foreach_descendant (account, [&count](auto acc){ count++; });
    2994           26 :     return count;
    2995              : }
    2996              : 
    2997              : gint
    2998          549 : gnc_account_get_current_depth (const Account *account)
    2999              : {
    3000              :     AccountPrivate *priv;
    3001          549 :     int depth = 0;
    3002              : 
    3003          549 :     g_return_val_if_fail(GNC_IS_ACCOUNT(account), 0);
    3004              : 
    3005          549 :     priv = GET_PRIVATE(account);
    3006         2263 :     while (priv->parent && (priv->type != ACCT_TYPE_ROOT))
    3007              :     {
    3008         1714 :         account = priv->parent;
    3009         1714 :         priv = GET_PRIVATE(account);
    3010         1714 :         depth++;
    3011              :     }
    3012              : 
    3013          549 :     return depth;
    3014              : }
    3015              : 
    3016              : gint
    3017          644 : gnc_account_get_tree_depth (const Account *account)
    3018              : {
    3019              :     AccountPrivate *priv;
    3020          644 :     g_return_val_if_fail(GNC_IS_ACCOUNT(account), 0);
    3021              : 
    3022          644 :     priv = GET_PRIVATE(account);
    3023          644 :     if (!priv->children.size())
    3024          412 :         return 1;
    3025              : 
    3026          232 :     return 1 + std::accumulate (priv->children.begin(), priv->children.end(),
    3027          556 :                                 0, [](auto a, auto b)
    3028          788 :                                 { return std::max (a, gnc_account_get_tree_depth (b)); });
    3029              : }
    3030              : 
    3031              : GList *
    3032          770 : gnc_account_get_descendants (const Account *account)
    3033              : {
    3034          770 :     GList* list = nullptr;
    3035        12765 :     gnc_account_foreach_descendant (account, [&list](auto a){ list = g_list_prepend (list, a); });
    3036         1540 :     return g_list_reverse (list);
    3037              : }
    3038              : 
    3039              : GList *
    3040         2379 : gnc_account_get_descendants_sorted (const Account *account)
    3041              : {
    3042         2379 :     GList* list = nullptr;
    3043        13149 :     account_foreach_descendant_sorted (account, [&list](auto a){ list = g_list_prepend (list, a); });
    3044         4758 :     return g_list_reverse (list);
    3045              : }
    3046              : 
    3047              : // because gnc_account_lookup_by_name and gnc_account_lookup_by_code
    3048              : // are described in Account.h searching breadth-first until 4.6, and
    3049              : // accidentally modified to search depth-first from 4.7
    3050              : // onwards. Restore breath-first searching in 4.11 onwards to match
    3051              : // previous behaviour and function description in Account.h
    3052              : static gpointer
    3053         3931 : account_foreach_descendant_breadthfirst_until (const Account *acc,
    3054              :                                                AccountCb2 thunk,
    3055              :                                                gpointer user_data)
    3056              : {
    3057         3931 :     g_return_val_if_fail (GNC_IS_ACCOUNT(acc), nullptr);
    3058         3931 :     g_return_val_if_fail (thunk, nullptr);
    3059              : 
    3060         3931 :     auto& children{GET_PRIVATE(acc)->children};
    3061              : 
    3062        11991 :     for (auto acc : children)
    3063         4400 :         if (auto result = thunk (acc, user_data))
    3064          271 :             return result;
    3065              : 
    3066        10538 :     for (auto acc: children)
    3067         3655 :         if (auto result = account_foreach_descendant_breadthfirst_until (acc, thunk, user_data))
    3068          437 :             return result;
    3069              : 
    3070         3223 :     return nullptr;
    3071              : }
    3072              : 
    3073              : static gpointer
    3074         4369 : is_acct_name (Account *account, gpointer user_data)
    3075              : {
    3076         4369 :     auto name {static_cast<gchar*>(user_data)};
    3077         4369 :     return (g_strcmp0 (name, xaccAccountGetName (account)) ? nullptr : account);
    3078              : }
    3079              : 
    3080              : Account *
    3081          268 : gnc_account_lookup_by_name (const Account *parent, const char * name)
    3082              : {
    3083          268 :     return (Account*)account_foreach_descendant_breadthfirst_until (parent, is_acct_name, (char*)name);
    3084              : }
    3085              : 
    3086              : static gpointer
    3087           31 : is_acct_code (Account *account, gpointer user_data)
    3088              : {
    3089           31 :     auto name {static_cast<gchar*>(user_data)};
    3090           31 :     return (g_strcmp0 (name, xaccAccountGetCode (account)) ? nullptr : account);
    3091              : }
    3092              : 
    3093              : Account *
    3094            8 : gnc_account_lookup_by_code (const Account *parent, const char * code)
    3095              : {
    3096            8 :     return (Account*)account_foreach_descendant_breadthfirst_until (parent, is_acct_code, (char*)code);
    3097              : }
    3098              : 
    3099              : static gpointer
    3100            0 : is_opening_balance_account (Account* account, gpointer data)
    3101              : {
    3102            0 :     gnc_commodity* commodity = GNC_COMMODITY(data);
    3103            0 :     if (xaccAccountGetIsOpeningBalance(account) && gnc_commodity_equiv(commodity, xaccAccountGetCommodity(account)))
    3104            0 :         return account;
    3105            0 :     return nullptr;
    3106              : }
    3107              : 
    3108              : Account*
    3109            0 : gnc_account_lookup_by_opening_balance (Account* account, gnc_commodity* commodity)
    3110              : {
    3111            0 :     return (Account *)gnc_account_foreach_descendant_until (account, is_opening_balance_account, commodity);
    3112              : }
    3113              : 
    3114              : /********************************************************************\
    3115              :  * Fetch an account, given its full name                            *
    3116              : \********************************************************************/
    3117              : 
    3118              : static Account *
    3119           43 : gnc_account_lookup_by_full_name_helper (const Account *parent,
    3120              :                                         gchar **names)
    3121              : {
    3122           43 :     g_return_val_if_fail(GNC_IS_ACCOUNT(parent), nullptr);
    3123           43 :     g_return_val_if_fail(names, nullptr);
    3124              : 
    3125              :     /* Look for the first name in the children. */
    3126          160 :     for (auto account : GET_PRIVATE(parent)->children)
    3127              :     {
    3128          104 :         auto priv = GET_PRIVATE(account);
    3129          104 :         if (g_strcmp0(priv->accountName, names[0]) == 0)
    3130              :         {
    3131              :             /* We found an account.  If the next entry is nullptr, there is
    3132              :              * nothing left in the name, so just return the account. */
    3133           35 :             if (names[1] == nullptr)
    3134           30 :                 return account;
    3135              : 
    3136              :             /* No children?  We're done. */
    3137           22 :             if (priv->children.empty())
    3138            0 :                 return nullptr;
    3139              : 
    3140              :             /* There's stuff left to search for.  Search recursively. */
    3141           22 :             if (auto found = gnc_account_lookup_by_full_name_helper(account, &names[1]))
    3142           17 :                 return found;
    3143              :         }
    3144              :     }
    3145              : 
    3146           13 :     return nullptr;
    3147              : }
    3148              : 
    3149              : 
    3150              : Account *
    3151           18 : gnc_account_lookup_by_full_name (const Account *any_acc,
    3152              :                                  const gchar *name)
    3153              : {
    3154              :     const AccountPrivate *rpriv;
    3155              :     const Account *root;
    3156              :     Account *found;
    3157              :     gchar **names;
    3158              : 
    3159           18 :     g_return_val_if_fail(GNC_IS_ACCOUNT(any_acc), nullptr);
    3160           18 :     g_return_val_if_fail(name, nullptr);
    3161              : 
    3162           18 :     root = any_acc;
    3163           18 :     rpriv = GET_PRIVATE(root);
    3164           30 :     while (rpriv->parent)
    3165              :     {
    3166           12 :         root = rpriv->parent;
    3167           12 :         rpriv = GET_PRIVATE(root);
    3168              :     }
    3169           18 :     names = g_strsplit(name, gnc_get_account_separator_string(), -1);
    3170           18 :     found = gnc_account_lookup_by_full_name_helper(root, names);
    3171           18 :     g_strfreev(names);
    3172           18 :     return found;
    3173              : }
    3174              : 
    3175              : GList*
    3176          171 : gnc_account_lookup_by_type_and_commodity (Account* root,
    3177              :                                           const char* name,
    3178              :                                           GNCAccountType acctype,
    3179              :                                           gnc_commodity* commodity)
    3180              : {
    3181          171 :     GList *retval{};
    3182          171 :     auto rpriv{GET_PRIVATE(root)};
    3183          455 :     for (auto account : rpriv->children)
    3184              :     {
    3185          113 :         if (xaccAccountGetType (account) == acctype)
    3186              :         {
    3187          120 :             if (commodity &&
    3188           60 :                 !gnc_commodity_equiv(xaccAccountGetCommodity (account),
    3189              :                                      commodity))
    3190           10 :                 continue;
    3191              : 
    3192           50 :             if (name && strcmp(name, xaccAccountGetName(account)))
    3193           26 :                 continue;
    3194              : 
    3195           24 :             retval = g_list_prepend(retval, account);
    3196              :         }
    3197              :     }
    3198              : 
    3199          171 :     if (!retval) // Recurse through the children
    3200          358 :         for (auto account : rpriv->children)
    3201              :         {
    3202           64 :             auto result = gnc_account_lookup_by_type_and_commodity(account,
    3203              :                                                                    name,
    3204              :                                                                    acctype,
    3205              :                                                                    commodity);
    3206           64 :             if (result)
    3207            0 :                 retval = g_list_concat(result, retval);
    3208              :         }
    3209          171 :     return retval;
    3210              : }
    3211              : 
    3212              : void
    3213          150 : gnc_account_foreach_child (const Account *acc,
    3214              :                            AccountCb thunk,
    3215              :                            gpointer user_data)
    3216              : {
    3217          150 :     g_return_if_fail(GNC_IS_ACCOUNT(acc));
    3218          150 :     g_return_if_fail(thunk);
    3219          150 :     std::for_each (GET_PRIVATE(acc)->children.begin(), GET_PRIVATE(acc)->children.end(),
    3220           53 :                    [user_data, thunk](auto a){ thunk (a, user_data); });
    3221              : }
    3222              : 
    3223              : void
    3224          715 : gnc_account_foreach_descendant (const Account *acc,
    3225              :                                 AccountCb thunk,
    3226              :                                 gpointer user_data)
    3227              : {
    3228         3571 :     gnc_account_foreach_descendant (acc, [&](auto acc){ thunk (acc, user_data); });
    3229          715 : }
    3230              : 
    3231              : gpointer
    3232           91 : gnc_account_foreach_descendant_until (const Account *acc,
    3233              :                                       AccountCb2 thunk,
    3234              :                                       gpointer user_data)
    3235              : {
    3236           91 :     gpointer result {nullptr};
    3237              : 
    3238           91 :     g_return_val_if_fail (GNC_IS_ACCOUNT(acc), nullptr);
    3239           91 :     g_return_val_if_fail (thunk, nullptr);
    3240              : 
    3241          247 :     for (auto child : GET_PRIVATE(acc)->children)
    3242              :     {
    3243           87 :         result = thunk (child, user_data);
    3244           87 :         if (result) break;
    3245              : 
    3246           80 :         result = gnc_account_foreach_descendant_until (child, thunk, user_data);
    3247           80 :         if (result) break;
    3248              :     }
    3249              : 
    3250           91 :     return result;
    3251              : }
    3252              : 
    3253              : 
    3254              : GNCAccountType
    3255        16312 : xaccAccountGetType (const Account *acc)
    3256              : {
    3257        16312 :     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), ACCT_TYPE_NONE);
    3258        16312 :     return GET_PRIVATE(acc)->type;
    3259              : }
    3260              : 
    3261              : static const char*
    3262           40 : qofAccountGetTypeString (const Account *acc)
    3263              : {
    3264           40 :     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
    3265           40 :     return xaccAccountTypeEnumAsString(GET_PRIVATE(acc)->type);
    3266              : }
    3267              : 
    3268              : static void
    3269           42 : qofAccountSetType (Account *acc, const char *type_string)
    3270              : {
    3271           42 :     g_return_if_fail(GNC_IS_ACCOUNT(acc));
    3272           42 :     g_return_if_fail(type_string);
    3273           42 :     xaccAccountSetType(acc, xaccAccountStringToEnum(type_string));
    3274              : }
    3275              : 
    3276              : const char *
    3277        72633 : xaccAccountGetName (const Account *acc)
    3278              : {
    3279        72633 :     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
    3280        72633 :     return GET_PRIVATE(acc)->accountName;
    3281              : }
    3282              : 
    3283              : std::vector<const Account*>
    3284        10403 : gnc_account_get_all_parents (const Account *account)
    3285              : {
    3286        10403 :     std::vector<const Account*> rv;
    3287        38782 :     for (auto a = account; !gnc_account_is_root (a); a = gnc_account_get_parent (a))
    3288        28379 :         rv.push_back (a);
    3289        10403 :     return rv;
    3290            0 : }
    3291              : 
    3292              : gchar *
    3293        10186 : gnc_account_get_full_name(const Account *account)
    3294              : {
    3295              :     /* So much for hardening the API. Too many callers to this function don't
    3296              :      * bother to check if they have a non-nullptr pointer before calling. */
    3297        10186 :     if (nullptr == account)
    3298            1 :         return g_strdup("");
    3299              : 
    3300              :     /* errors */
    3301        10185 :     g_return_val_if_fail(GNC_IS_ACCOUNT(account), g_strdup(""));
    3302              : 
    3303        10185 :     auto path{gnc_account_get_all_parents (account)};
    3304        10185 :     auto seps_size{path.empty() ? 0 : strlen (account_separator) * (path.size() - 1)};
    3305        10185 :     auto alloc_size{std::accumulate (path.begin(), path.end(), seps_size,
    3306        27879 :                                      [](auto sum, auto acc)
    3307        27879 :                                      { return sum + strlen (xaccAccountGetName (acc)); })};
    3308        10185 :     auto rv = g_new (char, alloc_size + 1);
    3309        10185 :     auto p = rv;
    3310              : 
    3311        10185 :     std::for_each (path.rbegin(), path.rend(),
    3312        27879 :                    [&p, rv](auto a)
    3313              :                    {
    3314        27879 :                        if (p != rv)
    3315        17696 :                            p = stpcpy (p, account_separator);
    3316        27879 :                        p = stpcpy (p, xaccAccountGetName (a));
    3317        27879 :                    });
    3318        10185 :     *p = '\0';
    3319              : 
    3320        10185 :     return rv;
    3321        10185 : }
    3322              : 
    3323              : const char *
    3324         1982 : xaccAccountGetCode (const Account *acc)
    3325              : {
    3326         1982 :     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
    3327         1982 :     return GET_PRIVATE(acc)->accountCode;
    3328              : }
    3329              : 
    3330              : const char *
    3331         1699 : xaccAccountGetDescription (const Account *acc)
    3332              : {
    3333         1699 :     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
    3334         1699 :     return GET_PRIVATE(acc)->description;
    3335              : }
    3336              : 
    3337              : const char *
    3338            8 : xaccAccountGetColor (const Account *acc)
    3339              : {
    3340           24 :     return get_kvp_string_path (acc, {"color"});
    3341              : }
    3342              : 
    3343              : const char *
    3344            6 : xaccAccountGetFilter (const Account *acc)
    3345              : {
    3346           18 :     return get_kvp_string_path (acc, {"filter"});
    3347              : }
    3348              : 
    3349              : const char *
    3350            6 : xaccAccountGetSortOrder (const Account *acc)
    3351              : {
    3352           18 :     return get_kvp_string_path (acc, {"sort-order"});
    3353              : }
    3354              : 
    3355              : gboolean
    3356            3 : xaccAccountGetSortReversed (const Account *acc)
    3357              : {
    3358            9 :     return get_kvp_boolean_path (acc, {"sort-reversed"});
    3359              : }
    3360              : 
    3361              : const char *
    3362         1339 : xaccAccountGetNotes (const Account *acc)
    3363              : {
    3364         4017 :     return get_kvp_string_path (acc, {"notes"});
    3365              : }
    3366              : 
    3367              : const char *
    3368           81 : xaccAccountGetOnlineID (const Account *acc)
    3369              : {
    3370           81 :     g_return_val_if_fail (GNC_IS_ACCOUNT(acc), nullptr);
    3371          243 :     return get_kvp_string_path (acc, {KEY_ONLINE_ID});
    3372           81 : }
    3373              : 
    3374              : Account*
    3375            3 : xaccAccountGetAssociatedAccount (const Account *acc, const char *tag)
    3376              : {
    3377            3 :     g_return_val_if_fail (tag && *tag, nullptr);
    3378              : 
    3379           15 :     return get_kvp_account_path (acc, {"associated-account", tag});
    3380            6 : }
    3381              : 
    3382              : 
    3383              : gnc_commodity *
    3384         1363 : DxaccAccountGetCurrency (const Account *acc)
    3385              : {
    3386         4089 :     if (auto s = get_kvp_string_path (acc, {"old-currency"}))
    3387              :     {
    3388            0 :         auto table = gnc_commodity_table_get_table (qof_instance_get_book(acc));
    3389            0 :         return gnc_commodity_table_lookup_unique (table, s);
    3390              :     }
    3391              : 
    3392         1363 :     return nullptr;
    3393              : }
    3394              : 
    3395              : gnc_commodity *
    3396        84183 : xaccAccountGetCommodity (const Account *acc)
    3397              : {
    3398        84183 :     if (!GNC_IS_ACCOUNT(acc))
    3399            0 :         return nullptr;
    3400        84183 :     return GET_PRIVATE(acc)->commodity;
    3401              : }
    3402              : 
    3403          427 : gnc_commodity * gnc_account_get_currency_or_parent(const Account* account)
    3404              : {
    3405          427 :     g_return_val_if_fail (GNC_IS_ACCOUNT (account), nullptr);
    3406              : 
    3407          856 :     for (auto acc = account; acc; acc = gnc_account_get_parent (acc))
    3408          853 :         if (auto comm = xaccAccountGetCommodity (acc); gnc_commodity_is_currency (comm))
    3409          424 :             return comm;
    3410              : 
    3411            3 :     return nullptr; // no suitable commodity found.
    3412              : }
    3413              : 
    3414              : /********************************************************************\
    3415              : \********************************************************************/
    3416              : void
    3417            1 : gnc_account_set_start_balance (Account *acc, const gnc_numeric start_baln)
    3418              : {
    3419              :     AccountPrivate *priv;
    3420              : 
    3421            1 :     g_return_if_fail(GNC_IS_ACCOUNT(acc));
    3422              : 
    3423            1 :     priv = GET_PRIVATE(acc);
    3424            1 :     priv->starting_balance = start_baln;
    3425            1 :     priv->balance_dirty = TRUE;
    3426              : }
    3427              : 
    3428              : void
    3429            0 : gnc_account_set_start_cleared_balance (Account *acc,
    3430              :                                        const gnc_numeric start_baln)
    3431              : {
    3432              :     AccountPrivate *priv;
    3433              : 
    3434            0 :     g_return_if_fail(GNC_IS_ACCOUNT(acc));
    3435              : 
    3436            0 :     priv = GET_PRIVATE(acc);
    3437            0 :     priv->starting_cleared_balance = start_baln;
    3438            0 :     priv->balance_dirty = TRUE;
    3439              : }
    3440              : 
    3441              : void
    3442            0 : gnc_account_set_start_reconciled_balance (Account *acc,
    3443              :         const gnc_numeric start_baln)
    3444              : {
    3445              :     AccountPrivate *priv;
    3446              : 
    3447            0 :     g_return_if_fail(GNC_IS_ACCOUNT(acc));
    3448              : 
    3449            0 :     priv = GET_PRIVATE(acc);
    3450            0 :     priv->starting_reconciled_balance = start_baln;
    3451            0 :     priv->balance_dirty = TRUE;
    3452              : }
    3453              : 
    3454              : gnc_numeric
    3455           68 : xaccAccountGetBalance (const Account *acc)
    3456              : {
    3457           68 :     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
    3458           68 :     return GET_PRIVATE(acc)->balance;
    3459              : }
    3460              : 
    3461              : gnc_numeric
    3462           38 : xaccAccountGetClearedBalance (const Account *acc)
    3463              : {
    3464           38 :     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
    3465           38 :     return GET_PRIVATE(acc)->cleared_balance;
    3466              : }
    3467              : 
    3468              : gnc_numeric
    3469           20 : xaccAccountGetReconciledBalance (const Account *acc)
    3470              : {
    3471           20 :     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
    3472           20 :     return GET_PRIVATE(acc)->reconciled_balance;
    3473              : }
    3474              : 
    3475              : gnc_numeric
    3476            2 : xaccAccountGetProjectedMinimumBalance (const Account *acc)
    3477              : {
    3478            2 :     auto today{gnc_time64_get_today_end()};
    3479            2 :     std::optional<gnc_numeric> minimum;
    3480              : 
    3481            6 :     auto before_today_end = [&minimum, today](const Split *s) -> bool
    3482              :     {
    3483            6 :         auto bal{xaccSplitGetBalance(s)};
    3484            6 :         if (!minimum || gnc_numeric_compare (bal, *minimum) < 0)
    3485            3 :             minimum = bal;
    3486           12 :         return (xaccTransGetDate(xaccSplitGetParent(s)) < today);
    3487            2 :     };
    3488              :     // scan to find today's split, but we're really interested in the
    3489              :     // minimum balance
    3490            2 :     [[maybe_unused]] auto todays_split = gnc_account_find_split (acc, before_today_end, true);
    3491            4 :     return minimum ? *minimum : gnc_numeric_zero();
    3492              : }
    3493              : 
    3494              : gboolean
    3495            0 : xaccAccountHasStockSplit (const Account *acc)
    3496              : {
    3497            0 :     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), false);
    3498            0 :     return GET_PRIVATE(acc)->has_stock_split;
    3499              : }
    3500              : 
    3501              : 
    3502              : /********************************************************************\
    3503              : \********************************************************************/
    3504              : 
    3505              : static gnc_numeric
    3506         2936 : GetBalanceAsOfDate (Account *acc, time64 date, std::function<gnc_numeric(Split*)> split_to_numeric)
    3507              : {
    3508         2936 :     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
    3509              : 
    3510         2936 :     xaccAccountSortSplits (acc, TRUE); /* just in case, normally a noop */
    3511         2936 :     xaccAccountRecomputeBalance (acc); /* just in case, normally a noop */
    3512              : 
    3513         4204 :     auto is_before_date = [date](auto s) -> bool
    3514         4204 :     { return xaccTransGetDate(xaccSplitGetParent(s)) < date; };
    3515              : 
    3516         2936 :     auto latest_split{gnc_account_find_split (acc, is_before_date, true)};
    3517         2936 :     return latest_split ? split_to_numeric (latest_split) : gnc_numeric_zero();
    3518              : }
    3519              : 
    3520              : gnc_numeric
    3521          374 : xaccAccountGetBalanceAsOfDate (Account *acc, time64 date)
    3522              : {
    3523          374 :     return GetBalanceAsOfDate (acc, date, xaccSplitGetBalance);
    3524              : }
    3525              : 
    3526              : static gnc_numeric
    3527            0 : xaccAccountGetNoclosingBalanceAsOfDate (Account *acc, time64 date)
    3528              : {
    3529            0 :     return GetBalanceAsOfDate (acc, date, xaccSplitGetNoclosingBalance);
    3530              : }
    3531              : 
    3532              : gnc_numeric
    3533            0 : xaccAccountGetReconciledBalanceAsOfDate (Account *acc, time64 date)
    3534              : {
    3535            0 :     return GetBalanceAsOfDate (acc, date, xaccSplitGetReconciledBalance);
    3536              : }
    3537              : 
    3538              : /*
    3539              :  * Originally gsr_account_present_balance in gnc-split-reg.c
    3540              :  */
    3541              : gnc_numeric
    3542            1 : xaccAccountGetPresentBalance (const Account *acc)
    3543              : {
    3544            1 :     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
    3545              : 
    3546            1 :     return xaccAccountGetBalanceAsOfDate (GNC_ACCOUNT (acc),
    3547            1 :                                           gnc_time64_get_today_end ());
    3548              : }
    3549              : 
    3550              : 
    3551              : /********************************************************************\
    3552              : \********************************************************************/
    3553              : /* XXX TODO: These 'GetBal' routines should be moved to some
    3554              :  * utility area outside of the core account engine area.
    3555              :  */
    3556              : 
    3557              : /*
    3558              :  * Convert a balance from one currency to another.
    3559              :  */
    3560              : gnc_numeric
    3561            0 : xaccAccountConvertBalanceToCurrency(const Account *acc, /* for book */
    3562              :                                     gnc_numeric balance,
    3563              :                                     const gnc_commodity *balance_currency,
    3564              :                                     const gnc_commodity *new_currency)
    3565              : {
    3566              :     QofBook *book;
    3567              :     GNCPriceDB *pdb;
    3568              : 
    3569            0 :     if (gnc_numeric_zero_p (balance) ||
    3570            0 :             gnc_commodity_equiv (balance_currency, new_currency))
    3571            0 :         return balance;
    3572              : 
    3573            0 :     book = gnc_account_get_book (acc);
    3574            0 :     pdb = gnc_pricedb_get_db (book);
    3575              : 
    3576            0 :     balance = gnc_pricedb_convert_balance_latest_price(
    3577              :                   pdb, balance, balance_currency, new_currency);
    3578              : 
    3579            0 :     return balance;
    3580              : }
    3581              : 
    3582              : /*
    3583              :  * Convert a balance from one currency to another with price of
    3584              :  * a given date.
    3585              :  */
    3586              : gnc_numeric
    3587          732 : xaccAccountConvertBalanceToCurrencyAsOfDate(const Account *acc, /* for book */
    3588              :         gnc_numeric balance,
    3589              :         const gnc_commodity *balance_currency,
    3590              :         const gnc_commodity *new_currency,
    3591              :         time64 date)
    3592              : {
    3593              :     QofBook *book;
    3594              :     GNCPriceDB *pdb;
    3595              : 
    3596          884 :     if (gnc_numeric_zero_p (balance) ||
    3597          152 :             gnc_commodity_equiv (balance_currency, new_currency))
    3598          732 :         return balance;
    3599              : 
    3600            0 :     book = gnc_account_get_book (acc);
    3601            0 :     pdb = gnc_pricedb_get_db (book);
    3602              : 
    3603            0 :     balance = gnc_pricedb_convert_balance_nearest_before_price_t64 (
    3604              :                   pdb, balance, balance_currency, new_currency, date);
    3605              : 
    3606            0 :     return balance;
    3607              : }
    3608              : 
    3609              : /*
    3610              :  * Given an account and a GetBalanceFn pointer, extract the requested
    3611              :  * balance from the account and then convert it to the desired
    3612              :  * currency.
    3613              :  */
    3614              : static gnc_numeric
    3615            0 : xaccAccountGetXxxBalanceInCurrency (const Account *acc,
    3616              :                                     xaccGetBalanceFn fn,
    3617              :                                     const gnc_commodity *report_currency)
    3618              : {
    3619              :     AccountPrivate *priv;
    3620              :     gnc_numeric balance;
    3621              : 
    3622            0 :     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
    3623            0 :     g_return_val_if_fail(fn, gnc_numeric_zero());
    3624            0 :     g_return_val_if_fail(GNC_IS_COMMODITY(report_currency), gnc_numeric_zero());
    3625              : 
    3626            0 :     priv = GET_PRIVATE(acc);
    3627            0 :     balance = fn(acc);
    3628            0 :     balance = xaccAccountConvertBalanceToCurrency(acc, balance,
    3629            0 :               priv->commodity,
    3630              :               report_currency);
    3631            0 :     return balance;
    3632              : }
    3633              : 
    3634              : static gnc_numeric
    3635            0 : xaccAccountGetXxxBalanceAsOfDateInCurrency(Account *acc, time64 date,
    3636              :         xaccGetBalanceAsOfDateFn fn,
    3637              :         const gnc_commodity *report_commodity)
    3638              : {
    3639              :     AccountPrivate *priv;
    3640              : 
    3641            0 :     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
    3642            0 :     g_return_val_if_fail(fn, gnc_numeric_zero());
    3643            0 :     g_return_val_if_fail(GNC_IS_COMMODITY(report_commodity), gnc_numeric_zero());
    3644              : 
    3645            0 :     priv = GET_PRIVATE(acc);
    3646            0 :     return xaccAccountConvertBalanceToCurrencyAsOfDate(
    3647            0 :                acc, fn(acc, date), priv->commodity, report_commodity, date);
    3648              : }
    3649              : 
    3650              : /*
    3651              :  * Data structure used to pass various arguments into the following fn.
    3652              :  */
    3653              : typedef struct
    3654              : {
    3655              :     const gnc_commodity *currency;
    3656              :     gnc_numeric balance;
    3657              :     xaccGetBalanceFn fn;
    3658              :     xaccGetBalanceAsOfDateFn asOfDateFn;
    3659              :     time64 date;
    3660              : } CurrencyBalance;
    3661              : 
    3662              : 
    3663              : /*
    3664              :  * A helper function for iterating over all the accounts in a list or
    3665              :  * tree.  This function is called once per account, and sums up the
    3666              :  * values of all these accounts.
    3667              :  */
    3668              : static void
    3669            0 : xaccAccountBalanceHelper (Account *acc, gpointer data)
    3670              : {
    3671            0 :     CurrencyBalance *cb = static_cast<CurrencyBalance*>(data);
    3672              :     gnc_numeric balance;
    3673              : 
    3674            0 :     if (!cb->fn || !cb->currency)
    3675            0 :         return;
    3676            0 :     balance = xaccAccountGetXxxBalanceInCurrency (acc, cb->fn, cb->currency);
    3677            0 :     cb->balance = gnc_numeric_add (cb->balance, balance,
    3678            0 :                                    gnc_commodity_get_fraction (cb->currency),
    3679              :                                    GNC_HOW_RND_ROUND_HALF_UP);
    3680              : }
    3681              : 
    3682              : static void
    3683            0 : xaccAccountBalanceAsOfDateHelper (Account *acc, gpointer data)
    3684              : {
    3685            0 :     CurrencyBalance *cb = static_cast<CurrencyBalance*>(data);
    3686              :     gnc_numeric balance;
    3687              : 
    3688            0 :     g_return_if_fail (cb->asOfDateFn && cb->currency);
    3689              : 
    3690            0 :     balance = xaccAccountGetXxxBalanceAsOfDateInCurrency (
    3691              :                   acc, cb->date, cb->asOfDateFn, cb->currency);
    3692            0 :     cb->balance = gnc_numeric_add (cb->balance, balance,
    3693            0 :                                    gnc_commodity_get_fraction (cb->currency),
    3694              :                                    GNC_HOW_RND_ROUND_HALF_UP);
    3695              : }
    3696              : 
    3697              : 
    3698              : 
    3699              : /*
    3700              :  * Common function that iterates recursively over all accounts below
    3701              :  * the specified account.  It uses xaccAccountBalanceHelper to sum up
    3702              :  * the balances of all its children, and uses the specified function
    3703              :  * 'fn' for extracting the balance.  This function may extract the
    3704              :  * current value, the reconciled value, etc.
    3705              :  *
    3706              :  * If 'report_commodity' is nullptr, just use the account's commodity.
    3707              :  * If 'include_children' is FALSE, this function doesn't recurse at all.
    3708              :  */
    3709              : static gnc_numeric
    3710            0 : xaccAccountGetXxxBalanceInCurrencyRecursive (const Account *acc,
    3711              :         xaccGetBalanceFn fn,
    3712              :         const gnc_commodity *report_commodity,
    3713              :         gboolean include_children)
    3714              : {
    3715              :     gnc_numeric balance;
    3716              : 
    3717            0 :     if (!acc) return gnc_numeric_zero ();
    3718            0 :     if (!report_commodity)
    3719            0 :         report_commodity = xaccAccountGetCommodity (acc);
    3720            0 :     if (!report_commodity)
    3721            0 :         return gnc_numeric_zero();
    3722              : 
    3723            0 :     balance = xaccAccountGetXxxBalanceInCurrency (acc, fn, report_commodity);
    3724              : 
    3725              :     /* If needed, sum up the children converting to the *requested*
    3726              :        commodity. */
    3727            0 :     if (include_children)
    3728              :     {
    3729              : #ifdef _MSC_VER
    3730              :         /* MSVC compiler: Somehow, the struct initialization containing a
    3731              :            gnc_numeric doesn't work. As an exception, we hand-initialize
    3732              :            that member afterwards. */
    3733              :         CurrencyBalance cb = { report_commodity, { 0 }, fn, nullptr, 0 };
    3734              :         cb.balance = balance;
    3735              : #else
    3736            0 :         CurrencyBalance cb = { report_commodity, balance, fn, nullptr, 0 };
    3737              : #endif
    3738              : 
    3739            0 :         gnc_account_foreach_descendant (acc, xaccAccountBalanceHelper, &cb);
    3740            0 :         balance = cb.balance;
    3741              :     }
    3742              : 
    3743            0 :     return balance;
    3744              : }
    3745              : 
    3746              : static gnc_numeric
    3747            0 : xaccAccountGetXxxBalanceAsOfDateInCurrencyRecursive (
    3748              :     Account *acc, time64 date, xaccGetBalanceAsOfDateFn fn,
    3749              :     const gnc_commodity *report_commodity, gboolean include_children)
    3750              : {
    3751              :     gnc_numeric balance;
    3752              : 
    3753            0 :     g_return_val_if_fail(acc, gnc_numeric_zero());
    3754            0 :     if (!report_commodity)
    3755            0 :         report_commodity = xaccAccountGetCommodity (acc);
    3756            0 :     if (!report_commodity)
    3757            0 :         return gnc_numeric_zero();
    3758              : 
    3759            0 :     balance = xaccAccountGetXxxBalanceAsOfDateInCurrency(
    3760              :                   acc, date, fn, report_commodity);
    3761              : 
    3762              :     /* If needed, sum up the children converting to the *requested*
    3763              :        commodity. */
    3764            0 :     if (include_children)
    3765              :     {
    3766              : #ifdef _MSC_VER
    3767              :         /* MSVC compiler: Somehow, the struct initialization containing a
    3768              :            gnc_numeric doesn't work. As an exception, we hand-initialize
    3769              :            that member afterwards. */
    3770              :         CurrencyBalance cb = { report_commodity, 0, nullptr, fn, date };
    3771              :         cb.balance = balance;
    3772              : #else
    3773            0 :         CurrencyBalance cb = { report_commodity, balance, nullptr, fn, date };
    3774              : #endif
    3775              : 
    3776            0 :         gnc_account_foreach_descendant (acc, xaccAccountBalanceAsOfDateHelper, &cb);
    3777            0 :         balance = cb.balance;
    3778              :     }
    3779              : 
    3780            0 :     return balance;
    3781              : }
    3782              : 
    3783              : gnc_numeric
    3784            0 : xaccAccountGetBalanceInCurrency (const Account *acc,
    3785              :                                  const gnc_commodity *report_commodity,
    3786              :                                  gboolean include_children)
    3787              : {
    3788              :     gnc_numeric rc;
    3789            0 :     rc = xaccAccountGetXxxBalanceInCurrencyRecursive (
    3790              :              acc, xaccAccountGetBalance, report_commodity, include_children);
    3791            0 :     PINFO(" baln=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, rc.num, rc.denom);
    3792            0 :     return rc;
    3793              : }
    3794              : 
    3795              : 
    3796              : gnc_numeric
    3797            0 : xaccAccountGetClearedBalanceInCurrency (const Account *acc,
    3798              :                                         const gnc_commodity *report_commodity,
    3799              :                                         gboolean include_children)
    3800              : {
    3801            0 :     return xaccAccountGetXxxBalanceInCurrencyRecursive (
    3802              :                acc, xaccAccountGetClearedBalance, report_commodity,
    3803            0 :                include_children);
    3804              : }
    3805              : 
    3806              : gnc_numeric
    3807            0 : xaccAccountGetReconciledBalanceInCurrency (const Account *acc,
    3808              :         const gnc_commodity *report_commodity,
    3809              :         gboolean include_children)
    3810              : {
    3811            0 :     return xaccAccountGetXxxBalanceInCurrencyRecursive (
    3812              :                acc, xaccAccountGetReconciledBalance, report_commodity,
    3813            0 :                include_children);
    3814              : }
    3815              : 
    3816              : gnc_numeric
    3817            0 : xaccAccountGetPresentBalanceInCurrency (const Account *acc,
    3818              :                                         const gnc_commodity *report_commodity,
    3819              :                                         gboolean include_children)
    3820              : {
    3821            0 :     return xaccAccountGetXxxBalanceAsOfDateInCurrencyRecursive (
    3822              :                (Account*)acc, gnc_time64_get_today_end (), xaccAccountGetBalanceAsOfDate,
    3823              :                report_commodity,
    3824            0 :                include_children);
    3825              : }
    3826              : 
    3827              : gnc_numeric
    3828            0 : xaccAccountGetProjectedMinimumBalanceInCurrency (
    3829              :     const Account *acc,
    3830              :     const gnc_commodity *report_commodity,
    3831              :     gboolean include_children)
    3832              : {
    3833            0 :     return xaccAccountGetXxxBalanceInCurrencyRecursive (
    3834              :                acc, xaccAccountGetProjectedMinimumBalance, report_commodity,
    3835            0 :                include_children);
    3836              : }
    3837              : 
    3838              : gnc_numeric
    3839            0 : xaccAccountGetBalanceAsOfDateInCurrency(
    3840              :     Account *acc, time64 date, gnc_commodity *report_commodity,
    3841              :     gboolean include_children)
    3842              : {
    3843            0 :     return xaccAccountGetXxxBalanceAsOfDateInCurrencyRecursive (
    3844              :                acc, date, xaccAccountGetBalanceAsOfDate, report_commodity,
    3845            0 :                include_children);
    3846              : }
    3847              : 
    3848              : gnc_numeric
    3849            0 : xaccAccountGetNoclosingBalanceAsOfDateInCurrency(
    3850              :     Account *acc, time64 date, gnc_commodity *report_commodity,
    3851              :     gboolean include_children)
    3852              : {
    3853              :     return xaccAccountGetXxxBalanceAsOfDateInCurrencyRecursive
    3854            0 :       (acc, date, xaccAccountGetNoclosingBalanceAsOfDate,
    3855            0 :        report_commodity, include_children);
    3856              : }
    3857              : 
    3858              : gnc_numeric
    3859            0 : xaccAccountGetBalanceChangeForPeriod (Account *acc, time64 t1, time64 t2,
    3860              :                                       gboolean recurse)
    3861              : {
    3862              :     gnc_numeric b1, b2;
    3863              : 
    3864            0 :     b1 = xaccAccountGetBalanceAsOfDateInCurrency(acc, t1, nullptr, recurse);
    3865            0 :     b2 = xaccAccountGetBalanceAsOfDateInCurrency(acc, t2, nullptr, recurse);
    3866            0 :     return gnc_numeric_sub(b2, b1, GNC_DENOM_AUTO, GNC_HOW_DENOM_FIXED);
    3867              : }
    3868              : 
    3869              : gnc_numeric
    3870            0 : xaccAccountGetNoclosingBalanceChangeForPeriod (Account *acc, time64 t1,
    3871              :                                                time64 t2, gboolean recurse)
    3872              : {
    3873              :     gnc_numeric b1, b2;
    3874              : 
    3875            0 :     b1 = xaccAccountGetNoclosingBalanceAsOfDateInCurrency(acc, t1, nullptr, recurse);
    3876            0 :     b2 = xaccAccountGetNoclosingBalanceAsOfDateInCurrency(acc, t2, nullptr, recurse);
    3877            0 :     return gnc_numeric_sub(b2, b1, GNC_DENOM_AUTO, GNC_HOW_DENOM_FIXED);
    3878              : }
    3879              : 
    3880              : typedef struct
    3881              : {
    3882              :     const gnc_commodity *currency;
    3883              :     gnc_numeric balanceChange;
    3884              :     time64 t1;
    3885              :     time64 t2;
    3886              : } CurrencyBalanceChange;
    3887              : 
    3888              : static void
    3889          732 : xaccAccountBalanceChangeHelper (Account *acc, gpointer data)
    3890              : {
    3891          732 :     CurrencyBalanceChange *cbdiff = static_cast<CurrencyBalanceChange*>(data);
    3892              : 
    3893              :     gnc_numeric b1, b2;
    3894          732 :     b1 = GetBalanceAsOfDate(acc, cbdiff->t1, xaccSplitGetNoclosingBalance);
    3895          732 :     b2 = GetBalanceAsOfDate(acc, cbdiff->t2, xaccSplitGetNoclosingBalance);
    3896          732 :     gnc_numeric balanceChange = gnc_numeric_sub(b2, b1, GNC_DENOM_AUTO, GNC_HOW_DENOM_FIXED);
    3897          732 :     gnc_numeric balanceChange_conv = xaccAccountConvertBalanceToCurrencyAsOfDate(acc, balanceChange, xaccAccountGetCommodity(acc), cbdiff->currency, cbdiff->t2);
    3898          732 :     cbdiff->balanceChange = gnc_numeric_add (cbdiff->balanceChange, balanceChange_conv,
    3899          732 :                                 gnc_commodity_get_fraction (cbdiff->currency),
    3900              :                                 GNC_HOW_RND_ROUND_HALF_UP);
    3901          732 : }
    3902              : 
    3903              : gnc_numeric
    3904          549 : xaccAccountGetNoclosingBalanceChangeInCurrencyForPeriod (Account *acc, time64 t1,
    3905              :                                                time64 t2, gboolean recurse)
    3906              : {
    3907              :     
    3908              : 
    3909              :     gnc_numeric b1, b2;
    3910          549 :     b1 = GetBalanceAsOfDate(acc, t1, xaccSplitGetNoclosingBalance);
    3911          549 :     b2 = GetBalanceAsOfDate(acc, t2, xaccSplitGetNoclosingBalance);
    3912          549 :     gnc_numeric balanceChange = gnc_numeric_sub(b2, b1, GNC_DENOM_AUTO, GNC_HOW_DENOM_FIXED);
    3913              : 
    3914          549 :     gnc_commodity *report_commodity = xaccAccountGetCommodity(acc);
    3915          549 :     CurrencyBalanceChange cbdiff = { report_commodity, balanceChange, t1, t2 };
    3916              : 
    3917          549 :     if(recurse)
    3918              :     {
    3919          549 :         gnc_account_foreach_descendant (acc, xaccAccountBalanceChangeHelper, &cbdiff);
    3920          549 :         balanceChange = cbdiff.balanceChange;
    3921              :     }
    3922          549 :     return balanceChange;
    3923              : }
    3924              : 
    3925              : /********************************************************************\
    3926              : \********************************************************************/
    3927              : 
    3928              : const SplitsVec&
    3929         9462 : xaccAccountGetSplits (const Account *account)
    3930              : {
    3931         9462 :     static const SplitsVec empty;
    3932         9462 :     g_return_val_if_fail (GNC_IS_ACCOUNT(account), empty);
    3933         9462 :     return GET_PRIVATE(account)->splits;
    3934              : }
    3935              : 
    3936              : SplitList *
    3937            1 : xaccAccountGetSplitList (const Account *acc)
    3938              : {
    3939            1 :     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
    3940            1 :     auto priv{GET_PRIVATE(acc)};
    3941            2 :     return std::accumulate (priv->splits.rbegin(), priv->splits.rend(),
    3942            1 :                             static_cast<GList*>(nullptr), g_list_prepend);
    3943              : }
    3944              : 
    3945              : size_t
    3946           25 : xaccAccountGetSplitsSize (const Account *account)
    3947              : {
    3948           25 :     g_return_val_if_fail (GNC_IS_ACCOUNT(account), 0);
    3949           25 :     return GNC_IS_ACCOUNT(account) ? GET_PRIVATE(account)->splits.size() : 0;
    3950              : }
    3951              : 
    3952              : gboolean
    3953            0 : gnc_account_and_descendants_empty (Account *acc)
    3954              : {
    3955            0 :     g_return_val_if_fail (GNC_IS_ACCOUNT (acc), FALSE);
    3956            0 :     auto priv = GET_PRIVATE (acc);
    3957            0 :     if (!priv->splits.empty()) return FALSE;
    3958            0 :     return std::all_of (priv->children.begin(), priv->children.end(),
    3959            0 :                         gnc_account_and_descendants_empty);
    3960              : }
    3961              : 
    3962              : LotList *
    3963          468 : xaccAccountGetLotList (const Account *acc)
    3964              : {
    3965          468 :     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
    3966          468 :     return g_list_copy(GET_PRIVATE(acc)->lots);
    3967              : }
    3968              : 
    3969              : LotList *
    3970            5 : xaccAccountFindOpenLots (const Account *acc,
    3971              :                          gboolean (*match_func)(GNCLot *lot,
    3972              :                                  gpointer user_data),
    3973              :                          gpointer user_data, GCompareFunc sort_func)
    3974              : {
    3975              :     AccountPrivate *priv;
    3976              :     GList *lot_list;
    3977            5 :     GList *retval = nullptr;
    3978              : 
    3979            5 :     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
    3980              : 
    3981            5 :     priv = GET_PRIVATE(acc);
    3982           18 :     for (lot_list = priv->lots; lot_list; lot_list = lot_list->next)
    3983              :     {
    3984           13 :         GNCLot *lot = static_cast<GNCLot*>(lot_list->data);
    3985              : 
    3986              :         /* If this lot is closed, then ignore it */
    3987           13 :         if (gnc_lot_is_closed (lot))
    3988            5 :             continue;
    3989              : 
    3990            8 :         if (match_func && !(match_func)(lot, user_data))
    3991            2 :             continue;
    3992              : 
    3993              :         /* Ok, this is a valid lot.  Add it to our list of lots */
    3994            6 :         retval = g_list_prepend (retval, lot);
    3995              :     }
    3996              : 
    3997            5 :     if (sort_func)
    3998            1 :         retval = g_list_sort (retval, sort_func);
    3999              : 
    4000            5 :     return retval;
    4001              : }
    4002              : 
    4003              : gpointer
    4004           66 : xaccAccountForEachLot(const Account *acc,
    4005              :                       gpointer (*proc)(GNCLot *lot, void *data), void *data)
    4006              : {
    4007           66 :     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
    4008           66 :     g_return_val_if_fail(proc, nullptr);
    4009              : 
    4010          827 :     for (auto node = GET_PRIVATE(acc)->lots; node; node = node->next)
    4011          762 :         if (auto result = proc(GNC_LOT(node->data), data))
    4012            1 :             return result;
    4013              : 
    4014           65 :     return nullptr;
    4015              : }
    4016              : 
    4017              : 
    4018              : /********************************************************************\
    4019              : \********************************************************************/
    4020              : 
    4021              : /* These functions use interchange gint64 and gboolean.  Is that right? */
    4022              : gboolean
    4023            1 : xaccAccountGetTaxRelated (const Account *acc)
    4024              : {
    4025            3 :     return get_kvp_boolean_path(acc, {"tax-related"});
    4026              : }
    4027              : 
    4028              : void
    4029            0 : xaccAccountSetTaxRelated (Account *acc, gboolean tax_related)
    4030              : {
    4031            0 :     set_kvp_boolean_path(acc, {"tax-related"}, tax_related);
    4032            0 : }
    4033              : 
    4034              : const char *
    4035            7 : xaccAccountGetTaxUSCode (const Account *acc)
    4036              : {
    4037           21 :     return get_kvp_string_path (acc, {"tax-US", "code"});
    4038              : }
    4039              : 
    4040              : void
    4041            5 : xaccAccountSetTaxUSCode (Account *acc, const char *code)
    4042              : {
    4043           10 :     set_kvp_string_path (acc, {"tax-US", "code"}, code);
    4044            5 : }
    4045              : 
    4046              : const char *
    4047            7 : xaccAccountGetTaxUSPayerNameSource (const Account *acc)
    4048              : {
    4049           21 :     return get_kvp_string_path (acc, {"tax-US", "payer-name-source"});
    4050              : }
    4051              : 
    4052              : void
    4053            5 : xaccAccountSetTaxUSPayerNameSource (Account *acc, const char *source)
    4054              : {
    4055           10 :     set_kvp_string_path (acc, {"tax-US", "payer-name-source"}, source);
    4056            5 : }
    4057              : 
    4058              : gint64
    4059            1 : xaccAccountGetTaxUSCopyNumber (const Account *acc)
    4060              : {
    4061            2 :     auto copy_number = get_kvp_int64_path (acc, {"tax-US", "copy-number"});
    4062            2 :     return (copy_number && (*copy_number != 0)) ? *copy_number : 1;
    4063              : }
    4064              : 
    4065              : void
    4066            0 : xaccAccountSetTaxUSCopyNumber (Account *acc, gint64 copy_number)
    4067              : {
    4068            0 :     if (copy_number != 0)
    4069            0 :         set_kvp_int64_path (acc, {"tax-US", "copy-number"}, copy_number);
    4070              :     else
    4071              :         /* deletes KVP if it exists */
    4072            0 :         set_kvp_int64_path (acc, {"tax-US", "copy-number"}, std::nullopt);
    4073            0 : }
    4074              : 
    4075              : /*********************************************************************\
    4076              : \ ********************************************************************/
    4077              : 
    4078              : 
    4079           11 : const char *gnc_account_get_debit_string (GNCAccountType acct_type)
    4080              : {
    4081           11 :     if (gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_ACCOUNTING_LABELS))
    4082            0 :         return _(dflt_acct_debit_str);
    4083              : 
    4084           11 :     auto result = gnc_acct_debit_strs.find(acct_type);
    4085           11 :     if (result != gnc_acct_debit_strs.end())
    4086           11 :         return _(result->second);
    4087              :     else
    4088            0 :         return _(dflt_acct_debit_str);
    4089              : }
    4090              : 
    4091           11 : const char *gnc_account_get_credit_string (GNCAccountType acct_type)
    4092              : {
    4093           11 :     if (gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_ACCOUNTING_LABELS))
    4094            0 :         return _(dflt_acct_credit_str);
    4095              : 
    4096           11 :     auto result = gnc_acct_credit_strs.find(acct_type);
    4097           11 :     if (result != gnc_acct_credit_strs.end())
    4098           11 :         return _(result->second);
    4099              :     else
    4100            0 :         return _(dflt_acct_credit_str);
    4101              : }
    4102              : 
    4103              : /********************************************************************\
    4104              : \********************************************************************/
    4105              : 
    4106              : gboolean
    4107          209 : xaccAccountGetPlaceholder (const Account *acc)
    4108              : {
    4109          627 :     return get_kvp_boolean_path(acc, {"placeholder"});
    4110              : }
    4111              : 
    4112              : void
    4113          127 : xaccAccountSetPlaceholder (Account *acc, gboolean val)
    4114              : {
    4115          254 :     set_kvp_boolean_path(acc, {"placeholder"}, val);
    4116          127 : }
    4117              : 
    4118              : gboolean
    4119            0 : xaccAccountGetAppendText (const Account *acc)
    4120              : {
    4121            0 :     return get_kvp_boolean_path(acc, {"import-append-text"});
    4122              : }
    4123              : 
    4124              : void
    4125            0 : xaccAccountSetAppendText (Account *acc, gboolean val)
    4126              : {
    4127            0 :     set_kvp_boolean_path(acc, {"import-append-text"}, val);
    4128            0 : }
    4129              : 
    4130              : gboolean
    4131            4 : xaccAccountGetIsOpeningBalance (const Account *acc)
    4132              : {
    4133            4 :     g_return_val_if_fail (GNC_IS_ACCOUNT(acc), false);
    4134            4 :     if (GET_PRIVATE(acc)->type != ACCT_TYPE_EQUITY)
    4135            1 :         return false;
    4136              : 
    4137            9 :     return !g_strcmp0 (get_kvp_string_path (acc, {"equity-type"}), "opening-balance");
    4138              : }
    4139              : 
    4140              : void
    4141            2 : xaccAccountSetIsOpeningBalance (Account *acc, gboolean val)
    4142              : {
    4143            2 :     g_return_if_fail (GNC_IS_ACCOUNT(acc));
    4144            2 :     if (GET_PRIVATE(acc)->type != ACCT_TYPE_EQUITY)
    4145            0 :         return;
    4146            6 :     set_kvp_string_path(acc, {"equity-type"}, val ? "opening-balance" : nullptr);
    4147              : }
    4148              : 
    4149              : GNCPlaceholderType
    4150            0 : xaccAccountGetDescendantPlaceholder (const Account *acc)
    4151              : {
    4152            0 :     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), PLACEHOLDER_NONE);
    4153            0 :     if (xaccAccountGetPlaceholder(acc)) return PLACEHOLDER_THIS;
    4154              : 
    4155            0 :     return gnc_account_foreach_descendant_until (acc, (AccountCb2)xaccAccountGetPlaceholder, nullptr)
    4156            0 :         ? PLACEHOLDER_CHILD : PLACEHOLDER_NONE;
    4157              : }
    4158              : 
    4159              : /********************************************************************\
    4160              :  \********************************************************************/
    4161              : 
    4162              : gboolean
    4163            0 : xaccAccountGetAutoInterest (const Account *acc)
    4164              : {
    4165            0 :     return get_kvp_boolean_path (acc, {KEY_RECONCILE_INFO, "auto-interest-transfer"});
    4166            0 : }
    4167              : 
    4168              : void
    4169            0 : xaccAccountSetAutoInterest (Account *acc, gboolean val)
    4170              : {
    4171            0 :     set_kvp_boolean_path (acc, {KEY_RECONCILE_INFO, "auto-interest-transfer"}, val);
    4172            0 : }
    4173              : 
    4174              : /********************************************************************\
    4175              : \********************************************************************/
    4176              : 
    4177              : gboolean
    4178           43 : xaccAccountGetHidden (const Account *acc)
    4179              : {
    4180          129 :     return get_kvp_boolean_path (acc, {"hidden"});
    4181              : }
    4182              : 
    4183              : void
    4184           46 : xaccAccountSetHidden (Account *acc, gboolean val)
    4185              : {
    4186           92 :     set_kvp_boolean_path (acc, {"hidden"}, val);
    4187           46 : }
    4188              : 
    4189              : gboolean
    4190            0 : xaccAccountIsHidden (const Account *acc)
    4191              : {
    4192              :     AccountPrivate *priv;
    4193              : 
    4194            0 :     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
    4195              : 
    4196            0 :     if (xaccAccountGetHidden(acc))
    4197            0 :         return TRUE;
    4198            0 :     priv = GET_PRIVATE(acc);
    4199            0 :     while ((acc = priv->parent) != nullptr)
    4200              :     {
    4201            0 :         priv = GET_PRIVATE(acc);
    4202            0 :         if (xaccAccountGetHidden(acc))
    4203            0 :             return TRUE;
    4204              :     }
    4205            0 :     return FALSE;
    4206              : }
    4207              : 
    4208              : /********************************************************************\
    4209              : \********************************************************************/
    4210              : 
    4211              : gboolean
    4212            3 : xaccAccountHasAncestor (const Account *acc, const Account * ancestor)
    4213              : {
    4214              :     const Account *parent;
    4215              : 
    4216            3 :     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
    4217            3 :     g_return_val_if_fail(GNC_IS_ACCOUNT(ancestor), FALSE);
    4218              : 
    4219            3 :     parent = acc;
    4220           13 :     while (parent && parent != ancestor)
    4221           10 :         parent = GET_PRIVATE(parent)->parent;
    4222              : 
    4223            3 :     return (parent == ancestor);
    4224              : }
    4225              : 
    4226              : /********************************************************************\
    4227              : \********************************************************************/
    4228              : 
    4229              : /* You must edit the functions in this block in tandem.  KEEP THEM IN
    4230              :    SYNC! */
    4231              : 
    4232              : #define GNC_RETURN_ENUM_AS_STRING(x) case (ACCT_TYPE_ ## x): return #x;
    4233              : 
    4234              : const char *
    4235          314 : xaccAccountTypeEnumAsString(GNCAccountType type)
    4236              : {
    4237          314 :     switch (type)
    4238              :     {
    4239            1 :         GNC_RETURN_ENUM_AS_STRING(NONE);
    4240           26 :         GNC_RETURN_ENUM_AS_STRING(BANK);
    4241            6 :         GNC_RETURN_ENUM_AS_STRING(CASH);
    4242            4 :         GNC_RETURN_ENUM_AS_STRING(CREDIT);
    4243           13 :         GNC_RETURN_ENUM_AS_STRING(ASSET);
    4244           16 :         GNC_RETURN_ENUM_AS_STRING(LIABILITY);
    4245            2 :         GNC_RETURN_ENUM_AS_STRING(STOCK);
    4246            4 :         GNC_RETURN_ENUM_AS_STRING(MUTUAL);
    4247            4 :         GNC_RETURN_ENUM_AS_STRING(CURRENCY);
    4248           26 :         GNC_RETURN_ENUM_AS_STRING(INCOME);
    4249          164 :         GNC_RETURN_ENUM_AS_STRING(EXPENSE);
    4250           11 :         GNC_RETURN_ENUM_AS_STRING(EQUITY);
    4251            4 :         GNC_RETURN_ENUM_AS_STRING(RECEIVABLE);
    4252            3 :         GNC_RETURN_ENUM_AS_STRING(PAYABLE);
    4253           24 :         GNC_RETURN_ENUM_AS_STRING(ROOT);
    4254            1 :         GNC_RETURN_ENUM_AS_STRING(TRADING);
    4255            1 :         GNC_RETURN_ENUM_AS_STRING(CHECKING);
    4256            1 :         GNC_RETURN_ENUM_AS_STRING(SAVINGS);
    4257            1 :         GNC_RETURN_ENUM_AS_STRING(MONEYMRKT);
    4258            1 :         GNC_RETURN_ENUM_AS_STRING(CREDITLINE);
    4259            1 :     default:
    4260            1 :         PERR ("asked to translate unknown account type %d.\n", type);
    4261            1 :         break;
    4262              :     }
    4263            1 :     return(nullptr);
    4264              : }
    4265              : 
    4266              : #undef GNC_RETURN_ENUM_AS_STRING
    4267              : 
    4268              : #define GNC_RETURN_ON_MATCH(x) \
    4269              :   if(g_strcmp0(#x, (str)) == 0) { *type = ACCT_TYPE_ ## x; return(TRUE); }
    4270              : 
    4271              : gboolean
    4272         1293 : xaccAccountStringToType(const char* str, GNCAccountType *type)
    4273              : {
    4274              : 
    4275         1293 :     GNC_RETURN_ON_MATCH(NONE);
    4276         1292 :     GNC_RETURN_ON_MATCH(BANK);
    4277         1202 :     GNC_RETURN_ON_MATCH(CASH);
    4278         1186 :     GNC_RETURN_ON_MATCH(CREDIT);
    4279         1169 :     GNC_RETURN_ON_MATCH(ASSET);
    4280         1091 :     GNC_RETURN_ON_MATCH(LIABILITY);
    4281         1027 :     GNC_RETURN_ON_MATCH(STOCK);
    4282          989 :     GNC_RETURN_ON_MATCH(MUTUAL);
    4283          962 :     GNC_RETURN_ON_MATCH(CURRENCY);
    4284          953 :     GNC_RETURN_ON_MATCH(INCOME);
    4285          806 :     GNC_RETURN_ON_MATCH(EXPENSE);
    4286          141 :     GNC_RETURN_ON_MATCH(EQUITY);
    4287           99 :     GNC_RETURN_ON_MATCH(RECEIVABLE);
    4288           91 :     GNC_RETURN_ON_MATCH(PAYABLE);
    4289           86 :     GNC_RETURN_ON_MATCH(ROOT);
    4290            7 :     GNC_RETURN_ON_MATCH(TRADING);
    4291            6 :     GNC_RETURN_ON_MATCH(CHECKING);
    4292            5 :     GNC_RETURN_ON_MATCH(SAVINGS);
    4293            4 :     GNC_RETURN_ON_MATCH(MONEYMRKT);
    4294            3 :     GNC_RETURN_ON_MATCH(CREDITLINE);
    4295              : 
    4296            2 :     PERR("asked to translate unknown account type string %s.\n",
    4297              :          str ? str : "(null)");
    4298              : 
    4299            2 :     return(FALSE);
    4300              : }
    4301              : 
    4302              : #undef GNC_RETURN_ON_MATCH
    4303              : 
    4304              : /* impedance mismatch is a source of loss */
    4305              : GNCAccountType
    4306           62 : xaccAccountStringToEnum(const char* str)
    4307              : {
    4308              :     GNCAccountType type;
    4309              :     gboolean rc;
    4310           62 :     rc = xaccAccountStringToType(str, &type);
    4311           62 :     if (FALSE == rc) return ACCT_TYPE_INVALID;
    4312           62 :     return type;
    4313              : }
    4314              : 
    4315              : /********************************************************************\
    4316              : \********************************************************************/
    4317              : 
    4318              : static char const *
    4319              : account_type_name[NUM_ACCOUNT_TYPES] =
    4320              : {
    4321              :     N_("Bank"),
    4322              :     N_("Cash"),
    4323              :     N_("Asset"),
    4324              :     N_("Credit Card"),
    4325              :     N_("Liability"),
    4326              :     N_("Stock"),
    4327              :     N_("Mutual Fund"),
    4328              :     N_("Currency"),
    4329              :     N_("Income"),
    4330              :     N_("Expense"),
    4331              :     N_("Equity"),
    4332              :     N_("A/Receivable"),
    4333              :     N_("A/Payable"),
    4334              :     N_("Root"),
    4335              :     N_("Trading")
    4336              :     /*
    4337              :       N_("Checking"),
    4338              :       N_("Savings"),
    4339              :       N_("Money Market"),
    4340              :       N_("Credit Line")
    4341              :     */
    4342              : };
    4343              : 
    4344              : const char *
    4345         1399 : xaccAccountGetTypeStr(GNCAccountType type)
    4346              : {
    4347         1399 :     if (type < 0 || NUM_ACCOUNT_TYPES <= type ) return "";
    4348         1399 :     return _(account_type_name [type]);
    4349              : }
    4350              : 
    4351              : /********************************************************************\
    4352              : \********************************************************************/
    4353              : 
    4354              : guint32
    4355           14 : xaccAccountTypesCompatibleWith (GNCAccountType type)
    4356              : {
    4357           14 :     switch (type)
    4358              :     {
    4359            8 :         case ACCT_TYPE_BANK:
    4360              :         case ACCT_TYPE_CASH:
    4361              :         case ACCT_TYPE_ASSET:
    4362              :         case ACCT_TYPE_CREDIT:
    4363              :         case ACCT_TYPE_LIABILITY:
    4364              :         case ACCT_TYPE_INCOME:
    4365              :         case ACCT_TYPE_EXPENSE:
    4366              :         case ACCT_TYPE_EQUITY:
    4367              :             return
    4368              :             (1 << ACCT_TYPE_BANK)       |
    4369              :             (1 << ACCT_TYPE_CASH)       |
    4370              :             (1 << ACCT_TYPE_ASSET)      |
    4371              :             (1 << ACCT_TYPE_CREDIT)     |
    4372              :             (1 << ACCT_TYPE_LIABILITY)  |
    4373              :             (1 << ACCT_TYPE_INCOME)     |
    4374              :             (1 << ACCT_TYPE_EXPENSE)    |
    4375            8 :             (1 << ACCT_TYPE_EQUITY);
    4376            3 :         case ACCT_TYPE_STOCK:
    4377              :         case ACCT_TYPE_MUTUAL:
    4378              :         case ACCT_TYPE_CURRENCY:
    4379              :             return
    4380              :             (1 << ACCT_TYPE_STOCK)      |
    4381              :             (1 << ACCT_TYPE_MUTUAL)     |
    4382            3 :             (1 << ACCT_TYPE_CURRENCY);
    4383            1 :         case ACCT_TYPE_RECEIVABLE:
    4384            1 :             return (1 << ACCT_TYPE_RECEIVABLE);
    4385            1 :         case ACCT_TYPE_PAYABLE:
    4386            1 :             return (1 << ACCT_TYPE_PAYABLE);
    4387            1 :         case ACCT_TYPE_TRADING:
    4388            1 :             return (1 << ACCT_TYPE_TRADING);
    4389            0 :         default:
    4390            0 :             PERR("bad account type: %d", type);
    4391            0 :             return 0;
    4392              :     }
    4393              : }
    4394              : guint32
    4395          282 : xaccParentAccountTypesCompatibleWith (GNCAccountType type)
    4396              : {
    4397          282 :     switch (type)
    4398              :     {
    4399          200 :     case ACCT_TYPE_BANK:
    4400              :     case ACCT_TYPE_CASH:
    4401              :     case ACCT_TYPE_ASSET:
    4402              :     case ACCT_TYPE_STOCK:
    4403              :     case ACCT_TYPE_MUTUAL:
    4404              :     case ACCT_TYPE_CURRENCY:
    4405              :     case ACCT_TYPE_CREDIT:
    4406              :     case ACCT_TYPE_LIABILITY:
    4407              :     case ACCT_TYPE_RECEIVABLE:
    4408              :     case ACCT_TYPE_PAYABLE:
    4409              :         return
    4410              :             (1 << ACCT_TYPE_BANK)       |
    4411              :             (1 << ACCT_TYPE_CASH)       |
    4412              :             (1 << ACCT_TYPE_ASSET)      |
    4413              :             (1 << ACCT_TYPE_STOCK)      |
    4414              :             (1 << ACCT_TYPE_MUTUAL)     |
    4415              :             (1 << ACCT_TYPE_CURRENCY)   |
    4416              :             (1 << ACCT_TYPE_CREDIT)     |
    4417              :             (1 << ACCT_TYPE_LIABILITY)  |
    4418              :             (1 << ACCT_TYPE_RECEIVABLE) |
    4419              :             (1 << ACCT_TYPE_PAYABLE)    |
    4420          200 :             (1 << ACCT_TYPE_ROOT);
    4421           40 :     case ACCT_TYPE_INCOME:
    4422              :     case ACCT_TYPE_EXPENSE:
    4423              :         return
    4424              :             (1 << ACCT_TYPE_INCOME)     |
    4425              :             (1 << ACCT_TYPE_EXPENSE)    |
    4426           40 :             (1 << ACCT_TYPE_ROOT);
    4427           20 :     case ACCT_TYPE_EQUITY:
    4428              :         return
    4429              :             (1 << ACCT_TYPE_EQUITY)     |
    4430           20 :             (1 << ACCT_TYPE_ROOT);
    4431           20 :     case ACCT_TYPE_TRADING:
    4432              :         return
    4433              :             (1 << ACCT_TYPE_TRADING)    |
    4434           20 :             (1 << ACCT_TYPE_ROOT);
    4435            2 :     default:
    4436            2 :         PERR("bad account type: %d", type);
    4437            2 :         return 0;
    4438              :     }
    4439              : }
    4440              : 
    4441              : gboolean
    4442          280 : xaccAccountTypesCompatible (GNCAccountType parent_type,
    4443              :                             GNCAccountType child_type)
    4444              : {
    4445              :     /* ACCT_TYPE_NONE isn't compatible with anything, even ACCT_TYPE_NONE. */
    4446          280 :     if (parent_type == ACCT_TYPE_NONE || child_type == ACCT_TYPE_NONE)
    4447           14 :         return FALSE;
    4448              : 
    4449              :     /* ACCT_TYPE_ROOT can't have a parent account, and asking will raise
    4450              :      * an error. */
    4451          266 :     if (child_type == ACCT_TYPE_ROOT)
    4452            0 :         return FALSE;
    4453              : 
    4454          266 :     return ((xaccParentAccountTypesCompatibleWith (child_type) &
    4455          266 :              (1 << parent_type))
    4456          266 :             != 0);
    4457              : }
    4458              : 
    4459              : guint32
    4460            0 : xaccAccountTypesValid(void)
    4461              : {
    4462            0 :     guint32 mask = (1 << NUM_ACCOUNT_TYPES) - 1;
    4463            0 :     mask &= ~((1 << ACCT_TYPE_CURRENCY) |  /* DEPRECATED */
    4464              :               (1 << ACCT_TYPE_ROOT));      /* ROOT */
    4465              : 
    4466            0 :     return mask;
    4467              : }
    4468              : 
    4469            0 : gboolean xaccAccountIsAssetLiabType(GNCAccountType t)
    4470              : {
    4471            0 :     switch (t)
    4472              :     {
    4473            0 :     case ACCT_TYPE_RECEIVABLE:
    4474              :     case ACCT_TYPE_PAYABLE:
    4475            0 :         return FALSE;
    4476            0 :     default:
    4477            0 :         return (xaccAccountTypesCompatible(ACCT_TYPE_ASSET, t)
    4478            0 :                 || xaccAccountTypesCompatible(ACCT_TYPE_LIABILITY, t));
    4479              :     }
    4480              : }
    4481              : 
    4482              : GNCAccountType
    4483            0 : xaccAccountTypeGetFundamental (GNCAccountType t)
    4484              : {
    4485            0 :     switch (t)
    4486              :     {
    4487            0 :         case ACCT_TYPE_BANK:
    4488              :         case ACCT_TYPE_STOCK:
    4489              :         case ACCT_TYPE_MONEYMRKT:
    4490              :         case ACCT_TYPE_CHECKING:
    4491              :         case ACCT_TYPE_SAVINGS:
    4492              :         case ACCT_TYPE_MUTUAL:
    4493              :         case ACCT_TYPE_CURRENCY:
    4494              :         case ACCT_TYPE_CASH:
    4495              :         case ACCT_TYPE_ASSET:
    4496              :         case ACCT_TYPE_RECEIVABLE:
    4497            0 :             return ACCT_TYPE_ASSET;
    4498            0 :         case ACCT_TYPE_CREDIT:
    4499              :         case ACCT_TYPE_LIABILITY:
    4500              :         case ACCT_TYPE_PAYABLE:
    4501              :         case ACCT_TYPE_CREDITLINE:
    4502            0 :             return ACCT_TYPE_LIABILITY;
    4503            0 :         case ACCT_TYPE_INCOME:
    4504            0 :             return ACCT_TYPE_INCOME;
    4505            0 :         case ACCT_TYPE_EXPENSE:
    4506            0 :             return ACCT_TYPE_EXPENSE;
    4507            0 :         case ACCT_TYPE_EQUITY:
    4508            0 :             return ACCT_TYPE_EQUITY;
    4509            0 :         case ACCT_TYPE_TRADING:
    4510              :         default:
    4511            0 :             return ACCT_TYPE_NONE;
    4512              :     }
    4513              : }
    4514              : 
    4515          308 : gboolean xaccAccountIsAPARType(GNCAccountType t)
    4516              : {
    4517          308 :     switch (t)
    4518              :     {
    4519          123 :     case ACCT_TYPE_RECEIVABLE:
    4520              :     case ACCT_TYPE_PAYABLE:
    4521          123 :         return TRUE;
    4522          185 :     default:
    4523          185 :         return FALSE;
    4524              :     }
    4525              : }
    4526              : 
    4527            0 : gboolean xaccAccountIsEquityType(GNCAccountType t)
    4528              : {
    4529            0 :     switch (t)
    4530              :     {
    4531            0 :     case ACCT_TYPE_EQUITY:
    4532            0 :         return TRUE;
    4533            0 :     default:
    4534            0 :         return FALSE;
    4535              :     }
    4536              : }
    4537              : 
    4538              : gboolean
    4539          549 : xaccAccountIsPriced(const Account *acc)
    4540              : {
    4541              :     AccountPrivate *priv;
    4542              : 
    4543          549 :     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
    4544              : 
    4545          549 :     priv = GET_PRIVATE(acc);
    4546         1035 :     return (priv->type == ACCT_TYPE_STOCK || priv->type == ACCT_TYPE_MUTUAL ||
    4547         1035 :             priv->type == ACCT_TYPE_CURRENCY);
    4548              : }
    4549              : 
    4550              : /********************************************************************\
    4551              : \********************************************************************/
    4552              : 
    4553              : gboolean
    4554            1 : xaccAccountGetReconcileLastDate (const Account *acc, time64 *last_date)
    4555              : {
    4556            1 :     gboolean retval = FALSE;
    4557            4 :     auto date = get_kvp_int64_path (acc, {KEY_RECONCILE_INFO, "last-date"});
    4558              : 
    4559            1 :     if (date)
    4560              :     {
    4561            1 :         if (last_date)
    4562            1 :             *last_date = *date;
    4563            1 :         retval = TRUE;
    4564              :     }
    4565            1 :     return retval;
    4566            2 : }
    4567              : 
    4568              : /********************************************************************\
    4569              : \********************************************************************/
    4570              : 
    4571              : void
    4572            1 : xaccAccountSetReconcileLastDate (Account *acc, time64 last_date)
    4573              : {
    4574            5 :     set_kvp_int64_path (acc, {KEY_RECONCILE_INFO, "last-date"}, last_date);
    4575            3 : }
    4576              : 
    4577              : /********************************************************************\
    4578              : \********************************************************************/
    4579              : 
    4580              : gboolean
    4581            2 : xaccAccountGetReconcileLastInterval (const Account *acc,
    4582              :                                      int *months, int *days)
    4583              : {
    4584            2 :     if (!acc) return FALSE;
    4585           10 :     auto m{get_kvp_int64_path (acc, {KEY_RECONCILE_INFO, "last-interval", "months"})};
    4586           10 :     auto d{get_kvp_int64_path (acc, {KEY_RECONCILE_INFO, "last-interval", "days"})};
    4587            2 :     if (m && d)
    4588              :     {
    4589            1 :         if (months)
    4590            1 :             *months = *m;
    4591            1 :         if (days)
    4592            1 :             *days = *d;
    4593            1 :         return true;
    4594              :     }
    4595            1 :     return false;
    4596            8 : }
    4597              : 
    4598              : /********************************************************************\
    4599              : \********************************************************************/
    4600              : 
    4601              : void
    4602            1 : xaccAccountSetReconcileLastInterval (Account *acc, int months, int days)
    4603              : {
    4604            6 :     set_kvp_int64_path (acc, {KEY_RECONCILE_INFO, "last-interval", "months"}, months);
    4605            6 :     set_kvp_int64_path (acc, {KEY_RECONCILE_INFO, "last-interval", "days"}, days);
    4606            5 : }
    4607              : 
    4608              : /********************************************************************\
    4609              : \********************************************************************/
    4610              : 
    4611              : gboolean
    4612            1 : xaccAccountGetReconcilePostponeDate (const Account *acc, time64 *postpone_date)
    4613              : {
    4614            6 :     if (auto date = get_kvp_int64_path (acc, {KEY_RECONCILE_INFO, KEY_POSTPONE, "date"}))
    4615              :     {
    4616            1 :         if (postpone_date)
    4617            1 :             *postpone_date = *date;
    4618            1 :         return true;
    4619              :     }
    4620            0 :     return false;
    4621            2 : }
    4622              : 
    4623              : /********************************************************************\
    4624              : \********************************************************************/
    4625              : 
    4626              : void
    4627            1 : xaccAccountSetReconcilePostponeDate (Account *acc, time64 postpone_date)
    4628              : {
    4629            6 :     set_kvp_int64_path (acc, {KEY_RECONCILE_INFO, KEY_POSTPONE, "date"}, postpone_date);
    4630            3 : }
    4631              : 
    4632              : /********************************************************************\
    4633              : \********************************************************************/
    4634              : 
    4635              : gboolean
    4636            3 : xaccAccountGetReconcilePostponeBalance (const Account *acc,
    4637              :                                         gnc_numeric *balance)
    4638              : {
    4639           18 :     if (auto bal = get_kvp_gnc_numeric_path (acc, {KEY_RECONCILE_INFO, KEY_POSTPONE, "balance"}))
    4640              :     {
    4641            1 :         if (balance)
    4642            1 :             *balance = *bal;
    4643            1 :         return true;
    4644              :     }
    4645            2 :     return false;
    4646            6 : }
    4647              : 
    4648              : /********************************************************************\
    4649              : \********************************************************************/
    4650              : 
    4651              : void
    4652            1 : xaccAccountSetReconcilePostponeBalance (Account *acc, gnc_numeric balance)
    4653              : {
    4654            6 :     set_kvp_gnc_numeric_path (acc, {KEY_RECONCILE_INFO, KEY_POSTPONE, "balance"}, balance);
    4655            3 : }
    4656              : 
    4657              : /********************************************************************\
    4658              : 
    4659              : \********************************************************************/
    4660              : 
    4661              : void
    4662            1 : xaccAccountClearReconcilePostpone (Account *acc)
    4663              : {
    4664            5 :     set_kvp_gnc_numeric_path (acc, {KEY_RECONCILE_INFO, KEY_POSTPONE}, {});
    4665            2 : }
    4666              : 
    4667              : /********************************************************************\
    4668              : \********************************************************************/
    4669              : 
    4670              : const char *
    4671            6 : xaccAccountGetLastNum (const Account *acc)
    4672              : {
    4673           18 :     return get_kvp_string_path (acc, {"last-num"});
    4674              : }
    4675              : 
    4676              : /********************************************************************\
    4677              : \********************************************************************/
    4678              : 
    4679              : void
    4680          385 : xaccAccountSetLastNum (Account *acc, const char *num)
    4681              : {
    4682          770 :     set_kvp_string_path (acc, {"last-num"}, num);
    4683          385 : }
    4684              : 
    4685              : 
    4686              : /********************************************************************\
    4687              : \********************************************************************/
    4688              : 
    4689              : static bool
    4690            8 : get_balance_limit (const Account* acc, const std::string& key, gnc_numeric* balance)
    4691              : {
    4692           32 :     auto limit = get_kvp_gnc_numeric_path (acc, {KEY_BALANCE_LIMIT, key});
    4693            8 :     if (limit)
    4694            4 :         *balance = gnc_numeric_create (limit->num, limit->denom);
    4695           16 :     return limit.has_value();
    4696            8 : }
    4697              : 
    4698              : static void
    4699            6 : set_balance_limit (Account *acc, const std::string& key, std::optional<gnc_numeric> balance)
    4700              : {
    4701            6 :     if (balance && gnc_numeric_check (*balance))
    4702            2 :         return;
    4703           16 :     set_kvp_gnc_numeric_path (acc, {KEY_BALANCE_LIMIT, key}, balance);
    4704            4 : }
    4705              : 
    4706              : gboolean
    4707            4 : xaccAccountGetHigherBalanceLimit (const Account *acc,
    4708              :                                   gnc_numeric *balance)
    4709              : {
    4710            4 :     return get_balance_limit (acc, KEY_BALANCE_HIGHER_LIMIT_VALUE, balance);
    4711              : }
    4712              : 
    4713              : gboolean
    4714            4 : xaccAccountGetLowerBalanceLimit (const Account *acc,
    4715              :                                  gnc_numeric *balance)
    4716              : {
    4717            4 :     return get_balance_limit (acc, KEY_BALANCE_LOWER_LIMIT_VALUE, balance);
    4718              : }
    4719              : 
    4720              : void
    4721            2 : xaccAccountSetHigherBalanceLimit (Account *acc, gnc_numeric balance)
    4722              : {
    4723            2 :     set_balance_limit (acc, KEY_BALANCE_HIGHER_LIMIT_VALUE, balance);
    4724            2 : }
    4725              : 
    4726              : void
    4727            2 : xaccAccountSetLowerBalanceLimit (Account *acc, gnc_numeric balance)
    4728              : {
    4729            2 :     set_balance_limit (acc, KEY_BALANCE_LOWER_LIMIT_VALUE, balance);
    4730            2 : }
    4731              : 
    4732              : void
    4733            1 : xaccAccountClearHigherBalanceLimit (Account *acc)
    4734              : {
    4735            1 :     set_balance_limit (acc, KEY_BALANCE_HIGHER_LIMIT_VALUE, {});
    4736            1 : }
    4737              : 
    4738              : void
    4739            1 : xaccAccountClearLowerBalanceLimit (Account *acc)
    4740              : {
    4741            1 :     set_balance_limit (acc, KEY_BALANCE_LOWER_LIMIT_VALUE, {});
    4742            1 : }
    4743              : 
    4744              : gboolean
    4745            3 : xaccAccountGetIncludeSubAccountBalances (const Account *acc)
    4746              : {
    4747           12 :     return get_kvp_boolean_path (acc, {KEY_BALANCE_LIMIT, KEY_BALANCE_INCLUDE_SUB_ACCTS});
    4748            3 : }
    4749              : 
    4750              : void
    4751            2 : xaccAccountSetIncludeSubAccountBalances (Account *acc, gboolean inc_sub)
    4752              : {
    4753            8 :     set_kvp_boolean_path (acc, {KEY_BALANCE_LIMIT, KEY_BALANCE_INCLUDE_SUB_ACCTS}, inc_sub);
    4754            4 : }
    4755              : 
    4756              : /********************************************************************\
    4757              : \********************************************************************/
    4758              : 
    4759              : static Account *
    4760            5 : GetOrMakeOrphanAccount (Account *root, gnc_commodity * currency)
    4761              : {
    4762              :     char * accname;
    4763              :     Account * acc;
    4764              : 
    4765            5 :     g_return_val_if_fail (root, nullptr);
    4766              : 
    4767              :     /* build the account name */
    4768            5 :     if (!currency)
    4769              :     {
    4770            0 :         PERR ("No currency specified!");
    4771            0 :         return nullptr;
    4772              :     }
    4773              : 
    4774            5 :     accname = g_strconcat (_("Orphaned Gains"), "-",
    4775              :                            gnc_commodity_get_mnemonic (currency), nullptr);
    4776              : 
    4777              :     /* See if we've got one of these going already ... */
    4778            5 :     acc = gnc_account_lookup_by_name(root, accname);
    4779              : 
    4780            5 :     if (acc == nullptr)
    4781              :     {
    4782              :         /* Guess not. We'll have to build one. */
    4783            3 :         acc = xaccMallocAccount (gnc_account_get_book(root));
    4784            3 :         xaccAccountBeginEdit (acc);
    4785            3 :         xaccAccountSetName (acc, accname);
    4786            3 :         xaccAccountSetCommodity (acc, currency);
    4787            3 :         xaccAccountSetType (acc, ACCT_TYPE_INCOME);
    4788            3 :         xaccAccountSetDescription (acc, _("Realized Gain/Loss"));
    4789            3 :         xaccAccountSetNotes (acc,
    4790            3 :                              _("Realized Gains or Losses from "
    4791              :                                "Commodity or Trading Accounts "
    4792              :                                "that haven't been recorded elsewhere."));
    4793              : 
    4794              :         /* Hang the account off the root. */
    4795            3 :         gnc_account_append_child (root, acc);
    4796            3 :         xaccAccountCommitEdit (acc);
    4797              :     }
    4798              : 
    4799            5 :     g_free (accname);
    4800              : 
    4801            5 :     return acc;
    4802              : }
    4803              : 
    4804              : Account *
    4805            6 : xaccAccountGainsAccount (Account *acc, gnc_commodity *curr)
    4806              : {
    4807           30 :     Path path {KEY_LOT_MGMT, "gains-acct", gnc_commodity_get_unique_name (curr)};
    4808            6 :     auto gains_account = get_kvp_account_path (acc, path);
    4809              : 
    4810            6 :     if (gains_account == nullptr) /* No gains account for this currency */
    4811              :     {
    4812            5 :         gains_account = GetOrMakeOrphanAccount (gnc_account_get_root (acc), curr);
    4813            5 :         set_kvp_account_path (acc, path, gains_account);
    4814              :     }
    4815              : 
    4816            6 :     return gains_account;
    4817           18 : }
    4818              : 
    4819              : /********************************************************************\
    4820              : \********************************************************************/
    4821              : 
    4822              : void
    4823           16 : dxaccAccountSetPriceSrc(Account *acc, const char *src)
    4824              : {
    4825           16 :     if (!acc) return;
    4826              : 
    4827           16 :     if (xaccAccountIsPriced(acc))
    4828           15 :         set_kvp_string_path (acc, {"old-price-source"}, src);
    4829              : }
    4830              : 
    4831              : /********************************************************************\
    4832              : \********************************************************************/
    4833              : 
    4834              : const char*
    4835          428 : dxaccAccountGetPriceSrc(const Account *acc)
    4836              : {
    4837          428 :     if (!acc) return nullptr;
    4838              : 
    4839          428 :     if (!xaccAccountIsPriced(acc)) return nullptr;
    4840              : 
    4841           57 :     return get_kvp_string_path (acc, {"old-price-source"});
    4842              : }
    4843              : 
    4844              : /********************************************************************\
    4845              : \********************************************************************/
    4846              : 
    4847              : void
    4848           16 : dxaccAccountSetQuoteTZ(Account *acc, const char *tz)
    4849              : {
    4850           16 :     if (!acc) return;
    4851           16 :     if (!xaccAccountIsPriced(acc)) return;
    4852           15 :     set_kvp_string_path (acc, {"old-quote-tz"}, tz);
    4853              : }
    4854              : 
    4855              : /********************************************************************\
    4856              : \********************************************************************/
    4857              : 
    4858              : const char*
    4859            4 : dxaccAccountGetQuoteTZ(const Account *acc)
    4860              : {
    4861            4 :     if (!acc) return nullptr;
    4862            4 :     if (!xaccAccountIsPriced(acc)) return nullptr;
    4863           12 :     return get_kvp_string_path (acc, {"old-quote-tz"});
    4864              : }
    4865              : 
    4866              : /********************************************************************\
    4867              : \********************************************************************/
    4868              : 
    4869              : void
    4870            1 : xaccAccountSetReconcileChildrenStatus(Account *acc, gboolean status)
    4871              : {
    4872              :     /* Would have been nice to use G_TYPE_BOOLEAN, but the other
    4873              :      * boolean kvps save the value as "true" or "false" and that would
    4874              :      * be file-incompatible with this.
    4875              :      */
    4876            5 :     set_kvp_int64_path (acc, {KEY_RECONCILE_INFO, KEY_INCLUDE_CHILDREN}, status);
    4877            2 : }
    4878              : 
    4879              : /********************************************************************\
    4880              : \********************************************************************/
    4881              : 
    4882              : gboolean
    4883            2 : xaccAccountGetReconcileChildrenStatus(const Account *acc)
    4884              : {
    4885              :     /* access the account's kvp-data for status and return that, if no value
    4886              :      * is found then we can assume not to include the children, that being
    4887              :      * the default behaviour
    4888              :      */
    4889            8 :     return get_kvp_boolean_path (acc, {KEY_RECONCILE_INFO, KEY_INCLUDE_CHILDREN});
    4890            2 : }
    4891              : 
    4892              : /********************************************************************\
    4893              : \********************************************************************/
    4894              : 
    4895              : Split *
    4896            2 : xaccAccountFindSplitByDesc(const Account *acc, const char *description)
    4897              : {
    4898           10 :     auto has_description = [description](const Split* s) -> bool
    4899           10 :     { return !g_strcmp0 (description, xaccTransGetDescription (xaccSplitGetParent (s))); };
    4900            2 :     return gnc_account_find_split (acc, has_description, true);
    4901              : }
    4902              : 
    4903              : /* This routine is for finding a matching transaction in an account by
    4904              :  * matching on the description field. [CAS: The rest of this comment
    4905              :  * seems to belong somewhere else.] This routine is used for
    4906              :  * auto-filling in registers with a default leading account. The
    4907              :  * dest_trans is a transaction used for currency checking. */
    4908              : Transaction *
    4909            1 : xaccAccountFindTransByDesc(const Account *acc, const char *description)
    4910              : {
    4911            1 :     auto split = xaccAccountFindSplitByDesc (acc, description);
    4912            1 :     return split ? xaccSplitGetParent (split) : nullptr;
    4913              : }
    4914              : 
    4915              : /* ================================================================ */
    4916              : /* Concatenation, Merging functions                                */
    4917              : 
    4918              : void
    4919            1 : gnc_account_join_children (Account *to_parent, Account *from_parent)
    4920              : {
    4921              : 
    4922              :     /* errors */
    4923            1 :     g_return_if_fail(GNC_IS_ACCOUNT(to_parent));
    4924            1 :     g_return_if_fail(GNC_IS_ACCOUNT(from_parent));
    4925              : 
    4926              :     /* optimizations */
    4927            1 :     auto from_priv = GET_PRIVATE(from_parent);
    4928            1 :     if (from_priv->children.empty())
    4929            0 :         return;
    4930              : 
    4931            1 :     ENTER (" ");
    4932            1 :     auto children = from_priv->children;
    4933            4 :     for (auto child : children)
    4934            2 :         gnc_account_append_child(to_parent, child);
    4935            1 :     LEAVE (" ");
    4936            1 : }
    4937              : /********************************************************************\
    4938              : \********************************************************************/
    4939              : 
    4940              : void
    4941            3 : gnc_account_merge_children (Account *parent)
    4942              : {
    4943            3 :     g_return_if_fail(GNC_IS_ACCOUNT(parent));
    4944              : 
    4945            3 :     auto ppriv = GET_PRIVATE(parent);
    4946           24 :     for (auto it_a = ppriv->children.begin(); it_a != ppriv->children.end(); it_a++)
    4947              :     {
    4948            9 :         auto acc_a = *it_a;
    4949            9 :         auto priv_a = GET_PRIVATE(acc_a);
    4950           46 :         for (auto it_b = std::next(it_a); it_b != ppriv->children.end(); it_b++)
    4951              :         {
    4952           14 :             auto acc_b = *it_b;
    4953           14 :             auto priv_b = GET_PRIVATE(acc_b);
    4954           14 :             if (0 != null_strcmp(priv_a->accountName, priv_b->accountName))
    4955           13 :                 continue;
    4956            1 :             if (0 != null_strcmp(priv_a->accountCode, priv_b->accountCode))
    4957            0 :                 continue;
    4958            1 :             if (0 != null_strcmp(priv_a->description, priv_b->description))
    4959            0 :                 continue;
    4960            1 :             if (0 != null_strcmp(xaccAccountGetColor(acc_a),
    4961              :                                  xaccAccountGetColor(acc_b)))
    4962            0 :                 continue;
    4963            1 :             if (!gnc_commodity_equiv(priv_a->commodity, priv_b->commodity))
    4964            0 :                 continue;
    4965            1 :             if (0 != null_strcmp(xaccAccountGetNotes(acc_a),
    4966              :                                  xaccAccountGetNotes(acc_b)))
    4967            0 :                 continue;
    4968            1 :             if (priv_a->type != priv_b->type)
    4969            0 :                 continue;
    4970              : 
    4971              :             /* consolidate children */
    4972            1 :             if (!priv_b->children.empty())
    4973              :             {
    4974            1 :                 auto work = priv_b->children;
    4975            4 :                 for (auto w : work)
    4976            2 :                     gnc_account_append_child (acc_a, w);
    4977              : 
    4978            1 :                 qof_event_gen (&acc_a->inst, QOF_EVENT_MODIFY, nullptr);
    4979            1 :                 qof_event_gen (&acc_b->inst, QOF_EVENT_MODIFY, nullptr);
    4980            1 :             }
    4981              : 
    4982              :             /* recurse to do the children's children */
    4983            1 :             gnc_account_merge_children (acc_a);
    4984              : 
    4985              :             /* consolidate transactions */
    4986            1 :             while (!priv_b->splits.empty())
    4987            0 :                 xaccSplitSetAccount (priv_b->splits.front(), acc_a);
    4988              : 
    4989              :             /* move back one before removal. next iteration around the loop
    4990              :              * will get the node after node_b */
    4991              :             it_b--;
    4992              : 
    4993              :             /* The destroy function will remove from list -- node_a is ok,
    4994              :              * it's before node_b */
    4995            1 :             xaccAccountBeginEdit (acc_b);
    4996            1 :             xaccAccountDestroy (acc_b);
    4997              :         }
    4998              :     }
    4999              : }
    5000              : 
    5001              : /* ================================================================ */
    5002              : /* Transaction Traversal functions                                  */
    5003              : 
    5004              : 
    5005              : static void
    5006           23 : xaccSplitsBeginStagedTransactionTraversals (SplitsVec& splits)
    5007              : {
    5008           80 :     for (auto s : splits)
    5009              :     {
    5010           34 :         Transaction *trans = s->parent;
    5011              : 
    5012           34 :         if (trans)
    5013           34 :             trans->marker = 0;
    5014              :     }
    5015           23 : }
    5016              : 
    5017              : /* original function */
    5018              : void
    5019           23 : xaccAccountBeginStagedTransactionTraversals (const Account *account)
    5020              : {
    5021           23 :     if (!account)
    5022            0 :         return;
    5023           23 :     xaccSplitsBeginStagedTransactionTraversals(GET_PRIVATE (account)->splits);
    5024              : }
    5025              : 
    5026              : gboolean
    5027            0 : xaccTransactionTraverse (Transaction *trans, int stage)
    5028              : {
    5029            0 :     if (trans == nullptr) return FALSE;
    5030              : 
    5031            0 :     if (trans->marker < stage)
    5032              :     {
    5033            0 :         trans->marker = stage;
    5034            0 :         return TRUE;
    5035              :     }
    5036              : 
    5037            0 :     return FALSE;
    5038              : }
    5039              : 
    5040              : /* Replacement for xaccGroupBeginStagedTransactionTraversals */
    5041              : void
    5042           55 : gnc_account_tree_begin_staged_transaction_traversals (Account *account)
    5043              : {
    5044         1260 :     auto do_one_account = [](auto acc)
    5045         1260 :     { gnc_account_foreach_split (acc, [](auto s){ s->parent->marker = 0; }); };
    5046           55 :     gnc_account_foreach_descendant (account, do_one_account);
    5047           55 : }
    5048              : 
    5049              : int
    5050           23 : xaccAccountStagedTransactionTraversal (const Account *acc,
    5051              :                                        unsigned int stage,
    5052              :                                        TransactionCallback thunk,
    5053              :                                        void *cb_data)
    5054              : {
    5055           23 :     if (!acc) return 0;
    5056              : 
    5057              :     // iterate on copy of splits. some callers modify the splitsvec.
    5058           23 :     auto splits = GET_PRIVATE(acc)->splits;
    5059           73 :     for (auto s : splits)
    5060              :     {
    5061           28 :         auto trans = s->parent;
    5062           28 :         if (trans && (trans->marker < stage))
    5063              :         {
    5064           20 :             trans->marker = stage;
    5065           20 :             if (thunk)
    5066              :             {
    5067           20 :                 auto retval = thunk(trans, cb_data);
    5068           20 :                 if (retval) return retval;
    5069              :             }
    5070              :         }
    5071              :     }
    5072              : 
    5073           22 :     return 0;
    5074           23 : }
    5075              : 
    5076              : int
    5077         1307 : gnc_account_tree_staged_transaction_traversal (const Account *acc,
    5078              :         unsigned int stage,
    5079              :         TransactionCallback thunk,
    5080              :         void *cb_data)
    5081              : {
    5082              :     const AccountPrivate *priv;
    5083              :     Transaction *trans;
    5084              :     int retval;
    5085              : 
    5086         1307 :     if (!acc) return 0;
    5087              : 
    5088              :     /* depth first traversal */
    5089         1307 :     priv = GET_PRIVATE(acc);
    5090         3863 :     for (auto acc_p : priv->children)
    5091              :     {
    5092         1252 :         retval = gnc_account_tree_staged_transaction_traversal(acc_p, stage, thunk, cb_data);
    5093         1252 :         if (retval) return retval;
    5094              :     }
    5095              : 
    5096              :     /* Now this account */
    5097         4697 :     for (auto s : priv->splits)
    5098              :     {
    5099         2090 :         trans = s->parent;
    5100         2090 :         if (trans && (trans->marker < stage))
    5101              :         {
    5102         1013 :             trans->marker = stage;
    5103         1013 :             if (thunk)
    5104              :             {
    5105         1013 :                 retval = thunk(trans, cb_data);
    5106         1013 :                 if (retval) return retval;
    5107              :             }
    5108              :         }
    5109              :     }
    5110              : 
    5111         1303 :     return 0;
    5112              : }
    5113              : 
    5114              : time64
    5115            0 : gnc_account_get_earliest_date (const Account* account)
    5116              : {
    5117            0 :     g_return_val_if_fail (GNC_IS_ACCOUNT(account), INT64_MAX);
    5118            0 :     const auto& splits = xaccAccountGetSplits (account);
    5119            0 :     return splits.empty() ? INT64_MAX : xaccTransGetDate (xaccSplitGetParent (splits.front()));
    5120              : }
    5121              : 
    5122              : /********************************************************************\
    5123              : \********************************************************************/
    5124              : 
    5125              : int
    5126           55 : xaccAccountTreeForEachTransaction (Account *acc,
    5127              :                                    int (*proc)(Transaction *t, void *data),
    5128              :                                    void *data)
    5129              : {
    5130           55 :     if (!acc || !proc) return 0;
    5131              : 
    5132           55 :     gnc_account_tree_begin_staged_transaction_traversals (acc);
    5133           55 :     return gnc_account_tree_staged_transaction_traversal (acc, 42, proc, data);
    5134              : }
    5135              : 
    5136              : 
    5137              : gint
    5138           23 : xaccAccountForEachTransaction(const Account *acc, TransactionCallback proc,
    5139              :                               void *data)
    5140              : {
    5141           23 :     if (!acc || !proc) return 0;
    5142           23 :     xaccAccountBeginStagedTransactionTraversals (acc);
    5143           23 :     return xaccAccountStagedTransactionTraversal(acc, 42, proc, data);
    5144              : }
    5145              : 
    5146              : /* ================================================================ */
    5147              : /* The following functions are used by
    5148              :  * src/import-export/import-backend.c to manipulate the contra-account
    5149              :  * matching data. See src/import-export/import-backend.c for explanations.
    5150              :  */
    5151              : 
    5152              : #define IMAP_FRAME              "import-map"
    5153              : #define IMAP_FRAME_BAYES        "import-map-bayes"
    5154              : 
    5155              : /* Look up an Account in the map */
    5156              : Account*
    5157            7 : gnc_account_imap_find_account (Account *acc,
    5158              :                                const char *category,
    5159              :                                const char *key)
    5160              : {
    5161            7 :     if (!acc || !key) return nullptr;
    5162           30 :     std::vector<std::string> path {IMAP_FRAME};
    5163            6 :     if (category)
    5164            8 :         path.push_back (category);
    5165            6 :     path.push_back (key);
    5166            6 :     return get_kvp_account_path (acc, path);
    5167            6 : }
    5168              : 
    5169              : Account*
    5170            0 : gnc_account_imap_find_any (QofBook *book, const char* category, const char *key)
    5171              : {
    5172            0 :     Account *account = nullptr;
    5173              : 
    5174              :     /* Get list of Accounts */
    5175            0 :     auto root = gnc_book_get_root_account (book);
    5176            0 :     auto accts = gnc_account_get_descendants_sorted (root);
    5177              : 
    5178              :     /* Go through list of accounts */
    5179            0 :     for (auto ptr = accts; ptr; ptr = g_list_next (ptr))
    5180              :     {
    5181            0 :         auto tmp_acc = static_cast<Account*> (ptr->data);
    5182              : 
    5183            0 :         if (gnc_account_imap_find_account (tmp_acc, category, key))
    5184              :         {
    5185            0 :             account = tmp_acc;
    5186            0 :             break;
    5187              :         }
    5188              :     }
    5189            0 :     g_list_free (accts);
    5190              : 
    5191            0 : return account;
    5192              : }
    5193              : 
    5194              : /* Store an Account in the map */
    5195              : void
    5196            9 : gnc_account_imap_add_account (Account *acc,
    5197              :                               const char *category,
    5198              :                               const char *key,
    5199              :                               Account *added_acc)
    5200              : {
    5201            9 :     if (!acc || !key || !added_acc || !*key) return;
    5202              : 
    5203           42 :     auto path = category ? Path{IMAP_FRAME, category, key} : Path{IMAP_FRAME, key};
    5204              : 
    5205            7 :     set_kvp_account_path (acc, path, added_acc);
    5206           21 : }
    5207              : 
    5208              : /* Remove a reference to an Account in the map */
    5209              : void
    5210            4 : gnc_account_imap_delete_account (Account *acc,
    5211              :                                  const char *category,
    5212              :                                  const char *key)
    5213              : {
    5214            4 :     if (!acc || !key) return;
    5215              : 
    5216           18 :     auto path = category ? Path{IMAP_FRAME, category, key} : Path{IMAP_FRAME, key};
    5217            3 :     if (qof_instance_has_path_slot (QOF_INSTANCE (acc), path))
    5218              :     {
    5219            3 :         qof_instance_slot_path_delete (QOF_INSTANCE (acc), path);
    5220            3 :         if (category)
    5221           10 :             qof_instance_slot_path_delete_if_empty (QOF_INSTANCE (acc), {IMAP_FRAME, category});
    5222            9 :         qof_instance_slot_path_delete_if_empty (QOF_INSTANCE (acc), {IMAP_FRAME});
    5223              :     }
    5224            3 :     qof_instance_set_dirty (QOF_INSTANCE (acc));
    5225           13 : }
    5226              : 
    5227              : /*--------------------------------------------------------------------------
    5228              :  Below here is the bayes transaction to account matching system
    5229              : --------------------------------------------------------------------------*/
    5230              : 
    5231              : 
    5232              : /** intermediate values used to calculate the bayes probability of a given account
    5233              :   where p(AB) = (a*b)/[a*b + (1-a)(1-b)], product is (a*b),
    5234              :   product_difference is (1-a) * (1-b)
    5235              :  */
    5236              : struct AccountProbability
    5237              : {
    5238              :     double product; /* product of probabilities */
    5239              :     double product_difference; /* product of (1-probabilities) */
    5240              : };
    5241              : 
    5242              : struct AccountTokenCount
    5243              : {
    5244              :     std::string account_guid;
    5245              :     int64_t token_count; /** occurrences of a given token for this account_guid */
    5246              : };
    5247              : 
    5248              : /** total_count and the token_count for a given account let us calculate the
    5249              :  * probability of a given account with any single token
    5250              :  */
    5251              : struct TokenAccountsInfo
    5252              : {
    5253              :     std::vector<AccountTokenCount> accounts;
    5254              :     int64_t total_count;
    5255              : };
    5256              : 
    5257              : /** holds an account guid and its corresponding integer probability
    5258              :   the integer probability is some factor of 10
    5259              :  */
    5260              : struct AccountInfo
    5261              : {
    5262              :     std::string account_guid;
    5263              :     int32_t probability;
    5264              : };
    5265              : 
    5266              : static void
    5267           11 : build_token_info(char const * suffix, KvpValue * value, TokenAccountsInfo & tokenInfo)
    5268              : {
    5269           11 :     if (strlen(suffix) == GUID_ENCODING_LENGTH)
    5270              :     {
    5271           10 :         tokenInfo.total_count += value->get<int64_t>();
    5272              :         /*By convention, the key ends with the account GUID.*/
    5273           30 :         tokenInfo.accounts.emplace_back(AccountTokenCount{std::string{suffix}, value->get<int64_t>()});
    5274              :     }
    5275           11 : }
    5276              : 
    5277              : /** We scale the probability values by probability_factor.
    5278              :   ie. with probability_factor of 100000, 10% would be
    5279              :   0.10 * 100000 = 10000 */
    5280              : static constexpr int probability_factor = 100000;
    5281              : 
    5282              : static FinalProbabilityVec
    5283            8 : build_probabilities(ProbabilityVec const & first_pass)
    5284              : {
    5285            8 :     FinalProbabilityVec ret;
    5286           24 :     for (auto const & first_pass_prob : first_pass)
    5287              :     {
    5288            8 :         auto const & account_probability = first_pass_prob.second;
    5289              :         /* P(AB) = A*B / [A*B + (1-A)*(1-B)]
    5290              :          * NOTE: so we only keep track of a running product(A*B*C...)
    5291              :          * and product difference ((1-A)(1-B)...)
    5292              :          */
    5293            8 :         int32_t probability = (account_probability.product /
    5294            8 :                 (account_probability.product + account_probability.product_difference)) * probability_factor;
    5295            8 :         ret.push_back({first_pass_prob.first, probability});
    5296              :     }
    5297            8 :     return ret;
    5298            0 : }
    5299              : 
    5300              : static AccountInfo
    5301            8 : highest_probability(FinalProbabilityVec const & probabilities)
    5302              : {
    5303           16 :     AccountInfo ret {"", std::numeric_limits<int32_t>::min()};
    5304           24 :     for (auto const & prob : probabilities)
    5305            8 :         if (prob.second > ret.probability)
    5306            8 :             ret = AccountInfo {prob.first, prob.second};
    5307            8 :     return ret;
    5308            0 : }
    5309              : 
    5310              : static ProbabilityVec
    5311            9 : get_first_pass_probabilities(Account* acc, GList * tokens)
    5312              : {
    5313            9 :     ProbabilityVec ret;
    5314              :     /* find the probability for each account that contains any of the tokens
    5315              :      * in the input tokens list. */
    5316           21 :     for (auto current_token = tokens; current_token; current_token = current_token->next)
    5317              :     {
    5318           12 :         TokenAccountsInfo tokenInfo{};
    5319           24 :         auto path = std::string{IMAP_FRAME_BAYES "/"} + static_cast <char const *> (current_token->data) + "/";
    5320           12 :         qof_instance_foreach_slot_prefix (QOF_INSTANCE (acc), path, &build_token_info, tokenInfo);
    5321           34 :         for (auto const & current_account_token : tokenInfo.accounts)
    5322              :         {
    5323           10 :             auto item = std::find_if(ret.begin(), ret.end(), [&current_account_token]
    5324              :                 (std::pair<std::string, AccountProbability> const & a) {
    5325            2 :                     return current_account_token.account_guid == a.first;
    5326              :                 });
    5327           20 :             if (item != ret.end())
    5328              :             {/* This account is already in the map */
    5329            4 :                 item->second.product = ((double)current_account_token.token_count /
    5330            4 :                                       (double)tokenInfo.total_count) * item->second.product;
    5331            2 :                 item->second.product_difference = ((double)1 - ((double)current_account_token.token_count /
    5332            4 :                                               (double)tokenInfo.total_count)) * item->second.product_difference;
    5333              :             }
    5334              :             else
    5335              :             {
    5336              :                 /* add a new entry */
    5337              :                 AccountProbability new_probability;
    5338            8 :                 new_probability.product = ((double)current_account_token.token_count /
    5339            8 :                                       (double)tokenInfo.total_count);
    5340            8 :                 new_probability.product_difference = 1 - (new_probability.product);
    5341           16 :                 ret.push_back({current_account_token.account_guid, std::move(new_probability)});
    5342              :             }
    5343              :         } /* for all accounts in tokenInfo */
    5344           12 :     }
    5345            9 :     return ret;
    5346            0 : }
    5347              : 
    5348              : static std::string
    5349            5 : look_for_old_separator_descendants (Account *root, std::string const & full_name, const gchar *separator)
    5350              : {
    5351              :     GList *top_accounts, *ptr;
    5352            5 :     gint   found_len = 0;
    5353              :     gchar  found_sep;
    5354            5 :     top_accounts = gnc_account_get_descendants (root);
    5355            5 :     PINFO("Incoming full_name is '%s', current separator is '%s'", full_name.c_str (), separator);
    5356              :     /* Go through list of top level accounts */
    5357           75 :     for (ptr = top_accounts; ptr; ptr = g_list_next (ptr))
    5358              :     {
    5359           35 :         const gchar *name = xaccAccountGetName (static_cast <Account const *> (ptr->data));
    5360              :         // we are looking for the longest top level account that matches
    5361           35 :         if (g_str_has_prefix (full_name.c_str (), name))
    5362              :         {
    5363            6 :             gint name_len = strlen (name);
    5364            6 :             const gchar old_sep = full_name[name_len];
    5365            6 :             if (!g_ascii_isalnum (old_sep)) // test for non alpha numeric
    5366              :             {
    5367            6 :                 if (name_len > found_len)
    5368              :                 {
    5369            6 :                     found_sep = full_name[name_len];
    5370            6 :                     found_len = name_len;
    5371              :                 }
    5372              :             }
    5373              :         }
    5374              :     }
    5375            5 :     g_list_free (top_accounts); // Free the List
    5376            5 :     std::string new_name {full_name};
    5377            5 :     if (found_len > 1)
    5378            5 :         std::replace (new_name.begin (), new_name.end (), found_sep, *separator);
    5379            5 :     PINFO ("Return full_name is '%s'", new_name.c_str ());
    5380            5 :     return new_name;
    5381            0 : }
    5382              : 
    5383              : static std::string
    5384            6 : get_guid_from_account_name (Account * root, std::string const & name)
    5385              : {
    5386            6 :     auto map_account = gnc_account_lookup_by_full_name (root, name.c_str ());
    5387            6 :     if (!map_account)
    5388              :     {
    5389              :         auto temp_account_name = look_for_old_separator_descendants (root, name,
    5390            5 :              gnc_get_account_separator_string ());
    5391            5 :         map_account = gnc_account_lookup_by_full_name (root, temp_account_name.c_str ());
    5392            5 :     }
    5393            6 :     auto temp_guid = gnc::GUID {*xaccAccountGetGUID (map_account)};
    5394           12 :     return temp_guid.to_string ();
    5395              : }
    5396              : 
    5397              : static FlatKvpEntry
    5398            7 : convert_entry (KvpEntry entry, Account* root)
    5399              : {
    5400              :     /*We need to make a copy here.*/
    5401            7 :     auto account_name = entry.first.back();
    5402            7 :     if (!gnc::GUID::is_valid_guid (account_name))
    5403              :     {
    5404              :         /* Earlier version stored the account name in the import map, and
    5405              :          * there were early beta versions of 2.7 that stored a GUID.
    5406              :          * If there is no GUID, we assume it's an account name. */
    5407              :         /* Take off the account name and replace it with the GUID */
    5408            6 :         entry.first.pop_back();
    5409            6 :         auto guid_str = get_guid_from_account_name (root, account_name);
    5410            6 :         entry.first.emplace_back (guid_str);
    5411            6 :     }
    5412            7 :     std::string new_key {std::accumulate (entry.first.begin(), entry.first.end(), std::string {})};
    5413            7 :     new_key = IMAP_FRAME_BAYES + new_key;
    5414           14 :     return {new_key, entry.second};
    5415            7 : }
    5416              : 
    5417              : static std::vector<FlatKvpEntry>
    5418            2 : get_flat_imap (Account * acc)
    5419              : {
    5420            2 :     auto frame = qof_instance_get_slots (QOF_INSTANCE (acc));
    5421            4 :     auto slot = frame->get_slot ({IMAP_FRAME_BAYES});
    5422            2 :     if (!slot)
    5423            1 :         return {};
    5424            1 :     auto imap_frame = slot->get<KvpFrame*> ();
    5425            1 :     auto flat_kvp = imap_frame->flatten_kvp ();
    5426            1 :     auto root = gnc_account_get_root (acc);
    5427            1 :     std::vector <FlatKvpEntry> ret;
    5428            9 :     for (auto const & flat_entry : flat_kvp)
    5429              :     {
    5430            7 :         auto converted_entry = convert_entry (flat_entry, root);
    5431              :         /*If the entry was invalid, we don't perpetuate it.*/
    5432            7 :         if (converted_entry.first.size())
    5433            7 :             ret.emplace_back (converted_entry);
    5434            7 :     }
    5435            1 :     return ret;
    5436            1 : }
    5437              : 
    5438              : static bool
    5439           14 : convert_imap_account_bayes_to_flat (Account *acc)
    5440              : {
    5441           14 :     auto frame = qof_instance_get_slots (QOF_INSTANCE (acc));
    5442           14 :     if (!frame->get_keys().size())
    5443           12 :         return false;
    5444            2 :     auto flat_imap = get_flat_imap(acc);
    5445            2 :     if (!flat_imap.size ())
    5446            1 :         return false;
    5447            1 :     xaccAccountBeginEdit(acc);
    5448            2 :     frame->set({IMAP_FRAME_BAYES}, nullptr);
    5449            1 :     std::for_each(flat_imap.begin(), flat_imap.end(),
    5450            7 :                   [&frame] (FlatKvpEntry const & entry) {
    5451           21 :                       frame->set({entry.first.c_str()}, entry.second);
    5452           21 :                   });
    5453            1 :     qof_instance_set_dirty (QOF_INSTANCE (acc));
    5454            1 :     xaccAccountCommitEdit(acc);
    5455            1 :     return true;
    5456            2 : }
    5457              : 
    5458              : /*
    5459              :  * Checks for import map data and converts them when found.
    5460              :  */
    5461              : static bool
    5462            2 : imap_convert_bayes_to_flat (QofBook * book)
    5463              : {
    5464            2 :     auto root = gnc_book_get_root_account (book);
    5465            2 :     auto accts = gnc_account_get_descendants_sorted (root);
    5466            2 :     bool ret = false;
    5467           30 :     for (auto ptr = accts; ptr; ptr = g_list_next (ptr))
    5468              :     {
    5469           14 :         Account *acc = static_cast <Account*> (ptr->data);
    5470           14 :         if (convert_imap_account_bayes_to_flat (acc))
    5471              :         {
    5472            1 :             ret = true;
    5473            1 :             gnc_features_set_used (book, GNC_FEATURE_GUID_FLAT_BAYESIAN);
    5474              :         }
    5475              :     }
    5476            2 :     g_list_free (accts);
    5477            2 :     return ret;
    5478              : }
    5479              : 
    5480              : void
    5481            1 : gnc_account_reset_convert_bayes_to_flat (void)
    5482              : {
    5483            1 :     imap_convert_bayes_to_flat_run = false;
    5484            1 : }
    5485              : 
    5486              : /*
    5487              :  * Here we check to see the state of import map data.
    5488              :  *
    5489              :  * If the GUID_FLAT_BAYESIAN feature flag is set, everything
    5490              :  * should be fine.
    5491              :  *
    5492              :  * If it is not set, there are two possibilities: import data
    5493              :  * are present from a previous version or not. If they are,
    5494              :  * they are converted, and the feature flag set. If there are
    5495              :  * no previous data, nothing is done.
    5496              :  */
    5497              : static void
    5498           21 : check_import_map_data (QofBook *book)
    5499              : {
    5500           21 :     if (gnc_features_check_used (book, GNC_FEATURE_GUID_FLAT_BAYESIAN) ||
    5501              :         imap_convert_bayes_to_flat_run)
    5502           19 :         return;
    5503              : 
    5504              :     /* This function will set GNC_FEATURE_GUID_FLAT_BAYESIAN if necessary.*/
    5505            2 :     imap_convert_bayes_to_flat (book);
    5506            2 :     imap_convert_bayes_to_flat_run = true;
    5507              : }
    5508              : 
    5509              : static constexpr double threshold = .90 * probability_factor; /* 90% */
    5510              : 
    5511              : /** Look up an Account in the map */
    5512              : Account*
    5513            9 : gnc_account_imap_find_account_bayes (Account *acc, GList *tokens)
    5514              : {
    5515            9 :     if (!acc)
    5516            0 :         return nullptr;
    5517            9 :     auto book = gnc_account_get_book(acc);
    5518            9 :     check_import_map_data (book);
    5519            9 :     auto first_pass = get_first_pass_probabilities(acc, tokens);
    5520            9 :     if (!first_pass.size())
    5521            1 :         return nullptr;
    5522            8 :     auto final_probabilities = build_probabilities(first_pass);
    5523            8 :     if (!final_probabilities.size())
    5524            0 :         return nullptr;
    5525            8 :     auto best = highest_probability(final_probabilities);
    5526            8 :     if (best.account_guid == "")
    5527            0 :         return nullptr;
    5528            8 :     if (best.probability < threshold)
    5529            0 :         return nullptr;
    5530            8 :     gnc::GUID guid;
    5531              :     try {
    5532            8 :         guid = gnc::GUID::from_string(best.account_guid);
    5533            0 :     } catch (gnc::guid_syntax_exception&) {
    5534            0 :         return nullptr;
    5535            0 :     }
    5536            8 :     auto account = xaccAccountLookup (reinterpret_cast<GncGUID*>(&guid), book);
    5537            8 :     return account;
    5538            9 : }
    5539              : 
    5540              : static void
    5541           14 : change_imap_entry (Account *acc, std::string const & path, int64_t token_count)
    5542              : {
    5543           14 :     PINFO("Source Account is '%s', Count is '%" G_GINT64_FORMAT "'",
    5544              :            xaccAccountGetName (acc), token_count);
    5545              : 
    5546              :     // check for existing guid entry
    5547           42 :     if (auto existing_token_count = get_kvp_int64_path (acc, {path}))
    5548              :     {
    5549            5 :         PINFO("found existing value of '%" G_GINT64_FORMAT "'", *existing_token_count);
    5550            5 :         token_count += *existing_token_count;
    5551              :     }
    5552              : 
    5553              :     // Add or Update the entry based on guid
    5554           56 :     set_kvp_int64_path (acc, {path}, token_count);
    5555           42 : }
    5556              : 
    5557              : /** Updates the imap for a given account using a list of tokens */
    5558              : void
    5559           11 : gnc_account_imap_add_account_bayes (Account *acc,
    5560              :                                     GList *tokens,
    5561              :                                     Account *added_acc)
    5562              : {
    5563              :     GList *current_token;
    5564              :     gint64 token_count;
    5565              :     char *account_fullname;
    5566              :     char *guid_string;
    5567              : 
    5568           11 :     ENTER(" ");
    5569           11 :     if (!acc)
    5570              :     {
    5571            0 :         LEAVE(" ");
    5572            0 :         return;
    5573              :     }
    5574           11 :     check_import_map_data (gnc_account_get_book(acc));
    5575              : 
    5576           11 :     g_return_if_fail (added_acc != nullptr);
    5577           10 :     account_fullname = gnc_account_get_full_name(added_acc);
    5578           10 :     xaccAccountBeginEdit (acc);
    5579              : 
    5580           10 :     PINFO("account name: '%s'", account_fullname);
    5581              : 
    5582           10 :     guid_string = guid_to_string (xaccAccountGetGUID (added_acc));
    5583              : 
    5584              :     /* process each token in the list */
    5585           24 :     for (current_token = g_list_first(tokens); current_token;
    5586           14 :             current_token = current_token->next)
    5587              :     {
    5588           14 :         char* token = static_cast<char*>(current_token->data);
    5589              :         /* Jump to next iteration if the pointer is not valid or if the
    5590              :                  string is empty. In HBCI import we almost always get an empty
    5591              :                  string, which doesn't work in the kvp loopkup later. So we
    5592              :                  skip this case here. */
    5593           14 :         if (!token || !token[0])
    5594            0 :             continue;
    5595              :         /* start off with one token for this account */
    5596           14 :         token_count = 1;
    5597           14 :         PINFO("adding token '%s'", token);
    5598           14 :         auto path = std::string {IMAP_FRAME_BAYES} + '/' + token + '/' + guid_string;
    5599              :         /* change the imap entry for the account */
    5600           14 :         change_imap_entry (acc, path, token_count);
    5601           14 :     }
    5602              :     /* free up the account fullname and guid string */
    5603           10 :     xaccAccountCommitEdit (acc);
    5604           10 :     gnc_features_set_used (gnc_account_get_book(acc), GNC_FEATURE_GUID_FLAT_BAYESIAN);
    5605           10 :     g_free (account_fullname);
    5606           10 :     g_free (guid_string);
    5607           10 :     LEAVE(" ");
    5608              : }
    5609              : 
    5610              : /*******************************************************************************/
    5611              : 
    5612              : static void
    5613            0 : build_non_bayes (const char *key, const GValue *value, gpointer user_data)
    5614              : {
    5615            0 :     if (!G_VALUE_HOLDS_BOXED (value))
    5616            0 :         return;
    5617              :     QofBook     *book;
    5618            0 :     GncGUID     *guid = nullptr;
    5619            0 :     gchar       *guid_string = nullptr;
    5620            0 :     auto imapInfo = (GncImapInfo*)user_data;
    5621              :     // Get the book
    5622            0 :     book = qof_instance_get_book (imapInfo->source_account);
    5623              : 
    5624            0 :     guid = (GncGUID*)g_value_get_boxed (value);
    5625            0 :     guid_string = guid_to_string (guid);
    5626              : 
    5627            0 :     PINFO("build_non_bayes: match string '%s', match account guid: '%s'",
    5628              :                             (char*)key, guid_string);
    5629              : 
    5630            0 :     auto imapInfo_node = static_cast <GncImapInfo*> (g_malloc(sizeof(GncImapInfo)));
    5631              : 
    5632            0 :     imapInfo_node->source_account = imapInfo->source_account;
    5633            0 :     imapInfo_node->map_account    = xaccAccountLookup (guid, book);
    5634            0 :     imapInfo_node->head           = g_strdup (imapInfo->head);
    5635            0 :     imapInfo_node->match_string   = g_strdup (key);
    5636            0 :     imapInfo_node->category       = g_strdup (imapInfo->category);
    5637            0 :     imapInfo_node->count          = g_strdup (" ");
    5638              : 
    5639            0 :     imapInfo->list = g_list_prepend (imapInfo->list, imapInfo_node);
    5640              : 
    5641            0 :     g_free (guid_string);
    5642              : }
    5643              : 
    5644              : static void
    5645            1 : build_bayes (const char *suffix, KvpValue * value, GncImapInfo & imapInfo)
    5646              : {
    5647            1 :     size_t guid_start = strlen(suffix) - GUID_ENCODING_LENGTH;
    5648            2 :     std::string account_guid {&suffix[guid_start]};
    5649              :     GncGUID guid;
    5650              :     try
    5651              :     {
    5652            1 :         guid = gnc::GUID::from_string (account_guid);
    5653              :     }
    5654            0 :     catch (const gnc::guid_syntax_exception& err)
    5655              :     {
    5656            0 :         PWARN("Invalid GUID string from %s%s", IMAP_FRAME_BAYES, suffix);
    5657            0 :     }
    5658            1 :     auto map_account = xaccAccountLookup (&guid, gnc_account_get_book (imapInfo.source_account));
    5659            1 :     auto imap_node = static_cast <GncImapInfo*> (g_malloc (sizeof (GncImapInfo)));
    5660            1 :     auto count = value->get <int64_t> ();
    5661            1 :     imap_node->source_account = imapInfo.source_account;
    5662            1 :     imap_node->map_account = map_account;
    5663            1 :     imap_node->head = g_strdup_printf ("%s%s", IMAP_FRAME_BAYES, suffix);
    5664            1 :     imap_node->match_string = g_strndup (&suffix[1], guid_start - 2);
    5665            1 :     imap_node->category = g_strdup(" ");
    5666            1 :     imap_node->count = g_strdup_printf ("%" G_GINT64_FORMAT, count);
    5667            1 :     imapInfo.list = g_list_prepend (imapInfo.list, imap_node);
    5668            1 : }
    5669              : 
    5670            1 : void gnc_account_imap_info_destroy (GncImapInfo* imapInfo)
    5671              : {
    5672            1 :     g_free (imapInfo->head);
    5673            1 :     g_free (imapInfo->category);
    5674            1 :     g_free (imapInfo->match_string);
    5675            1 :     g_free (imapInfo->count);
    5676            1 :     g_free (imapInfo);
    5677            1 : }
    5678              : 
    5679              : GList *
    5680            1 : gnc_account_imap_get_info_bayes (Account *acc)
    5681              : {
    5682            1 :     check_import_map_data (gnc_account_get_book (acc));
    5683              :     /* A dummy object which is used to hold the specified account, and the list
    5684              :      * of data about which we care. */
    5685            1 :     GncImapInfo imapInfo {acc, nullptr};
    5686            2 :     qof_instance_foreach_slot_prefix (QOF_INSTANCE (acc), IMAP_FRAME_BAYES, &build_bayes, imapInfo);
    5687            2 :     return g_list_reverse(imapInfo.list);
    5688              : }
    5689              : 
    5690              : GList *
    5691            0 : gnc_account_imap_get_info (Account *acc, const char *category)
    5692              : {
    5693            0 :     GList *list = nullptr;
    5694              : 
    5695              :     GncImapInfo imapInfo;
    5696              : 
    5697            0 :     std::vector<std::string> path {IMAP_FRAME};
    5698            0 :     if (category)
    5699            0 :         path.emplace_back (category);
    5700              : 
    5701            0 :     imapInfo.source_account = acc;
    5702            0 :     imapInfo.list = list;
    5703              : 
    5704            0 :     imapInfo.head = g_strdup (IMAP_FRAME);
    5705            0 :     imapInfo.category = g_strdup (category);
    5706              : 
    5707            0 :     if (qof_instance_has_path_slot (QOF_INSTANCE (acc), path))
    5708              :     {
    5709            0 :         qof_instance_foreach_slot (QOF_INSTANCE(acc), IMAP_FRAME, category,
    5710              :                                    build_non_bayes, &imapInfo);
    5711              :     }
    5712            0 :     g_free (imapInfo.head);
    5713            0 :     g_free (imapInfo.category);
    5714            0 :     return g_list_reverse(imapInfo.list);
    5715            0 : }
    5716              : 
    5717              : /*******************************************************************************/
    5718              : 
    5719              : gchar *
    5720            9 : gnc_account_get_map_entry (Account *acc, const char *head, const char *category)
    5721              : {
    5722           60 :     return g_strdup (category ?
    5723              :                      get_kvp_string_path (acc, {head, category}) :
    5724              :                      get_kvp_string_path (acc, {head}));
    5725           18 : }
    5726              : 
    5727              : GncGUID *
    5728            2 : gnc_account_get_map_guid_entry (Account *acc, const char *head, const char *category)
    5729              : {
    5730            2 :     return category ?
    5731            2 :            get_kvp_guid_path (acc, {head, category}) :
    5732           14 :            get_kvp_guid_path (acc, {head});
    5733            4 : }
    5734              : 
    5735              : void
    5736            1 : gnc_account_delete_map_entry (Account *acc, char *head, char *category,
    5737              :                               char *match_string, gboolean empty)
    5738              : {
    5739            1 :     if (acc != nullptr)
    5740              :     {
    5741            3 :         std::vector<std::string> path {head};
    5742            1 :         if (category)
    5743            0 :             path.emplace_back (category);
    5744            1 :         if (match_string)
    5745            0 :             path.emplace_back (match_string);
    5746              : 
    5747            1 :         if (qof_instance_has_path_slot (QOF_INSTANCE (acc), path))
    5748              :         {
    5749            1 :             xaccAccountBeginEdit (acc);
    5750            1 :             if (empty)
    5751            0 :                 qof_instance_slot_path_delete_if_empty (QOF_INSTANCE(acc), path);
    5752              :             else
    5753            1 :                 qof_instance_slot_path_delete (QOF_INSTANCE(acc), path);
    5754            1 :             PINFO("Account is '%s', head is '%s', category is '%s', match_string is'%s'",
    5755              :                    xaccAccountGetName (acc), head, category, match_string);
    5756            1 :             qof_instance_set_dirty (QOF_INSTANCE(acc));
    5757            1 :             xaccAccountCommitEdit (acc);
    5758              :         }
    5759            1 :     }
    5760            3 : }
    5761              : 
    5762              : void
    5763            0 : gnc_account_delete_all_bayes_maps (Account *acc)
    5764              : {
    5765            0 :     if (acc != nullptr)
    5766              :     {
    5767            0 :         auto slots = qof_instance_get_slots_prefix (QOF_INSTANCE (acc), IMAP_FRAME_BAYES);
    5768            0 :         if (!slots.size()) return;
    5769            0 :         xaccAccountBeginEdit (acc);
    5770            0 :         for (auto const & entry : slots)
    5771              :         {
    5772            0 :              qof_instance_slot_path_delete (QOF_INSTANCE (acc), {entry.first});
    5773              :         }
    5774            0 :         qof_instance_set_dirty (QOF_INSTANCE(acc));
    5775            0 :         xaccAccountCommitEdit (acc);
    5776            0 :     }
    5777            0 : }
    5778              : 
    5779              : /* ================================================================ */
    5780              : /* QofObject function implementation and registration */
    5781              : 
    5782              : static void
    5783         1418 : destroy_all_child_accounts (Account *acc, gpointer data)
    5784              : {
    5785         1418 :     xaccAccountBeginEdit (acc);
    5786         1418 :     xaccAccountDestroy (acc);
    5787         1418 : }
    5788              : 
    5789              : static void
    5790          179 : gnc_account_book_end(QofBook* book)
    5791              : {
    5792          179 :     Account *root_account = gnc_book_get_root_account (book);
    5793              :     GList *accounts;
    5794              : 
    5795          179 :     if (!root_account)
    5796           73 :         return;
    5797              : 
    5798          106 :     accounts = gnc_account_get_descendants (root_account);
    5799              : 
    5800          106 :     if (accounts)
    5801              :     {
    5802          104 :         accounts = g_list_reverse (accounts);
    5803          104 :         g_list_foreach (accounts, (GFunc)destroy_all_child_accounts, nullptr);
    5804          104 :         g_list_free (accounts);
    5805              :     }
    5806          106 :     xaccAccountBeginEdit (root_account);
    5807          106 :     xaccAccountDestroy (root_account);
    5808              : }
    5809              : 
    5810              : #ifdef _MSC_VER
    5811              : /* MSVC compiler doesn't have C99 "designated initializers"
    5812              :  * so we wrap them in a macro that is empty on MSVC. */
    5813              : # define DI(x) /* */
    5814              : #else
    5815              : # define DI(x) x
    5816              : #endif
    5817              : static QofObject account_object_def =
    5818              : {
    5819              :     DI(.interface_version = ) QOF_OBJECT_VERSION,
    5820              :     DI(.e_type            = ) GNC_ID_ACCOUNT,
    5821              :     DI(.type_label        = ) "Account",
    5822              :     DI(.create            = ) (void*(*)(QofBook*)) xaccMallocAccount,
    5823              :     DI(.book_begin        = ) nullptr,
    5824              :     DI(.book_end          = ) gnc_account_book_end,
    5825              :     DI(.is_dirty          = ) qof_collection_is_dirty,
    5826              :     DI(.mark_clean        = ) qof_collection_mark_clean,
    5827              :     DI(.foreach           = ) qof_collection_foreach,
    5828              :     DI(.printable         = ) (const char * (*)(gpointer)) xaccAccountGetName,
    5829              :     DI(.version_cmp       = ) (int (*)(gpointer, gpointer)) qof_instance_version_cmp,
    5830              : };
    5831              : 
    5832           83 : gboolean xaccAccountRegister (void)
    5833              : {
    5834              :     static QofParam params[] =
    5835              :     {
    5836              :         {
    5837              :             ACCOUNT_NAME_, QOF_TYPE_STRING,
    5838              :             (QofAccessFunc) xaccAccountGetName,
    5839              :             (QofSetterFunc) xaccAccountSetName
    5840              :         },
    5841              :         {
    5842              :             ACCOUNT_CODE_, QOF_TYPE_STRING,
    5843              :             (QofAccessFunc) xaccAccountGetCode,
    5844              :             (QofSetterFunc) xaccAccountSetCode
    5845              :         },
    5846              :         {
    5847              :             ACCOUNT_DESCRIPTION_, QOF_TYPE_STRING,
    5848              :             (QofAccessFunc) xaccAccountGetDescription,
    5849              :             (QofSetterFunc) xaccAccountSetDescription
    5850              :         },
    5851              :         {
    5852              :             ACCOUNT_COLOR_, QOF_TYPE_STRING,
    5853              :             (QofAccessFunc) xaccAccountGetColor,
    5854              :             (QofSetterFunc) xaccAccountSetColor
    5855              :         },
    5856              :         {
    5857              :             ACCOUNT_FILTER_, QOF_TYPE_STRING,
    5858              :             (QofAccessFunc) xaccAccountGetFilter,
    5859              :             (QofSetterFunc) xaccAccountSetFilter
    5860              :         },
    5861              :         {
    5862              :             ACCOUNT_SORT_ORDER_, QOF_TYPE_STRING,
    5863              :             (QofAccessFunc) xaccAccountGetSortOrder,
    5864              :             (QofSetterFunc) xaccAccountSetSortOrder
    5865              :         },
    5866              :         {
    5867              :             ACCOUNT_SORT_REVERSED_, QOF_TYPE_BOOLEAN,
    5868              :             (QofAccessFunc) xaccAccountGetSortReversed,
    5869              :             (QofSetterFunc) xaccAccountSetSortReversed
    5870              :         },
    5871              :         {
    5872              :             ACCOUNT_NOTES_, QOF_TYPE_STRING,
    5873              :             (QofAccessFunc) xaccAccountGetNotes,
    5874              :             (QofSetterFunc) xaccAccountSetNotes
    5875              :         },
    5876              :         {
    5877              :             ACCOUNT_PRESENT_, QOF_TYPE_NUMERIC,
    5878              :             (QofAccessFunc) xaccAccountGetPresentBalance, nullptr
    5879              :         },
    5880              :         {
    5881              :             ACCOUNT_BALANCE_, QOF_TYPE_NUMERIC,
    5882              :             (QofAccessFunc) xaccAccountGetBalance, nullptr
    5883              :         },
    5884              :         {
    5885              :             ACCOUNT_CLEARED_, QOF_TYPE_NUMERIC,
    5886              :             (QofAccessFunc) xaccAccountGetClearedBalance, nullptr
    5887              :         },
    5888              :         {
    5889              :             ACCOUNT_RECONCILED_, QOF_TYPE_NUMERIC,
    5890              :             (QofAccessFunc) xaccAccountGetReconciledBalance, nullptr
    5891              :         },
    5892              :         {
    5893              :             ACCOUNT_TYPE_, QOF_TYPE_STRING,
    5894              :             (QofAccessFunc) qofAccountGetTypeString,
    5895              :             (QofSetterFunc) qofAccountSetType
    5896              :         },
    5897              :         {
    5898              :             ACCOUNT_FUTURE_MINIMUM_, QOF_TYPE_NUMERIC,
    5899              :             (QofAccessFunc) xaccAccountGetProjectedMinimumBalance, nullptr
    5900              :         },
    5901              :         {
    5902              :             ACCOUNT_TAX_RELATED, QOF_TYPE_BOOLEAN,
    5903              :             (QofAccessFunc) xaccAccountGetTaxRelated,
    5904              :             (QofSetterFunc) xaccAccountSetTaxRelated
    5905              :         },
    5906              :         {
    5907              :             ACCOUNT_OPENING_BALANCE_, QOF_TYPE_BOOLEAN,
    5908              :             (QofAccessFunc) xaccAccountGetIsOpeningBalance,
    5909              :             (QofSetterFunc) xaccAccountSetIsOpeningBalance
    5910              :         },
    5911              :         {
    5912              :             ACCOUNT_SCU, QOF_TYPE_INT32,
    5913              :             (QofAccessFunc) xaccAccountGetCommoditySCU,
    5914              :             (QofSetterFunc) xaccAccountSetCommoditySCU
    5915              :         },
    5916              :         {
    5917              :             ACCOUNT_NSCU, QOF_TYPE_BOOLEAN,
    5918              :             (QofAccessFunc) xaccAccountGetNonStdSCU,
    5919              :             (QofSetterFunc) xaccAccountSetNonStdSCU
    5920              :         },
    5921              :         {
    5922              :             ACCOUNT_PARENT, GNC_ID_ACCOUNT,
    5923              :             (QofAccessFunc) gnc_account_get_parent,
    5924              :             (QofSetterFunc) qofAccountSetParent
    5925              :         },
    5926              :         {
    5927              :             QOF_PARAM_BOOK, QOF_ID_BOOK,
    5928              :             (QofAccessFunc) qof_instance_get_book, nullptr
    5929              :         },
    5930              :         {
    5931              :             QOF_PARAM_GUID, QOF_TYPE_GUID,
    5932              :             (QofAccessFunc) qof_instance_get_guid, nullptr
    5933              :         },
    5934              :         { nullptr },
    5935              :     };
    5936              : 
    5937           83 :     qof_class_register (GNC_ID_ACCOUNT, (QofSortFunc) qof_xaccAccountOrder, params);
    5938              : 
    5939           83 :     return qof_object_register (&account_object_def);
    5940              : }
    5941              : 
    5942              : /* ======================= UNIT TESTING ACCESS =======================
    5943              :  * The following functions are for unit testing use only.
    5944              :  */
    5945              : static AccountPrivate*
    5946           11 : utest_account_get_private (Account *acc)
    5947              : {
    5948           11 :     return GET_PRIVATE (acc);
    5949              : }
    5950              : 
    5951              : AccountTestFunctions*
    5952           45 : _utest_account_fill_functions(void)
    5953              : {
    5954           45 :     AccountTestFunctions* func = g_new(AccountTestFunctions, 1);
    5955              : 
    5956           45 :     func->get_private = utest_account_get_private;
    5957           45 :     func->coll_get_root_account = gnc_coll_get_root_account;
    5958           45 :     func->xaccFreeAccountChildren = xaccFreeAccountChildren;
    5959           45 :     func->xaccFreeAccount = xaccFreeAccount;
    5960           45 :     func->qofAccountSetParent = qofAccountSetParent;
    5961           45 :     func->gnc_account_lookup_by_full_name_helper =
    5962              :         gnc_account_lookup_by_full_name_helper;
    5963              : 
    5964           45 :     return func;
    5965              : }
    5966              : /* ======================= END OF FILE =========================== */
        

Generated by: LCOV version 2.0-1