LCOV - code coverage report
Current view: top level - libgnucash/engine - Account.cpp (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 76.9 % 2402 1848
Test Date: 2025-02-07 16:25:45 Functions: 81.6 % 309 252
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

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

Generated by: LCOV version 2.0-1