LCOV - code coverage report
Current view: top level - libgnucash/engine - Scrub.cpp (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 46.7 % 675 315
Test Date: 2025-08-26 06:50:40 Functions: 51.9 % 52 27
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : /********************************************************************\
       2                 :             :  * Scrub.c -- convert single-entry accounts into clean double-entry *
       3                 :             :  *                                                                  *
       4                 :             :  * This program is free software; you can redistribute it and/or    *
       5                 :             :  * modify it under the terms of the GNU General Public License as   *
       6                 :             :  * published by the Free Software Foundation; either version 2 of   *
       7                 :             :  * the License, or (at your option) any later version.              *
       8                 :             :  *                                                                  *
       9                 :             :  * This program is distributed in the hope that it will be useful,  *
      10                 :             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
      11                 :             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
      12                 :             :  * GNU General Public License for more details.                     *
      13                 :             :  *                                                                  *
      14                 :             :  * You should have received a copy of the GNU General Public License*
      15                 :             :  * along with this program; if not, contact:                        *
      16                 :             :  *                                                                  *
      17                 :             :  * Free Software Foundation           Voice:  +1-617-542-5942       *
      18                 :             :  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
      19                 :             :  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
      20                 :             :  *                                                                  *
      21                 :             : \********************************************************************/
      22                 :             : 
      23                 :             : /*
      24                 :             :  * FILE:
      25                 :             :  * Scrub.c
      26                 :             :  *
      27                 :             :  * FUNCTION:
      28                 :             :  * Provides a set of functions and utilities for scrubbing clean
      29                 :             :  * single-entry accounts so that they can be promoted into
      30                 :             :  * self-consistent, clean double-entry accounts.
      31                 :             :  *
      32                 :             :  * HISTORY:
      33                 :             :  * Created by Linas Vepstas December 1998
      34                 :             :  * Copyright (c) 1998-2000, 2003 Linas Vepstas <linas@linas.org>
      35                 :             :  * Copyright (c) 2002 Christian Stimming
      36                 :             :  * Copyright (c) 2006 David Hampton
      37                 :             :  */
      38                 :             : 
      39                 :             : #include <config.h>
      40                 :             : 
      41                 :             : #include <glib.h>
      42                 :             : #include <glib/gi18n.h>
      43                 :             : #include <stdio.h>
      44                 :             : #include <string.h>
      45                 :             : #include <stdint.h>
      46                 :             : #include <stdbool.h>
      47                 :             : #include <unordered_set>
      48                 :             : 
      49                 :             : #include "Account.h"
      50                 :             : #include "AccountP.hpp"
      51                 :             : #include "Account.hpp"
      52                 :             : #include "Scrub.h"
      53                 :             : #include "Transaction.h"
      54                 :             : #include "TransactionP.hpp"
      55                 :             : #include "gnc-commodity.h"
      56                 :             : #include "qofinstance-p.h"
      57                 :             : #include "gnc-session.h"
      58                 :             : 
      59                 :             : #undef G_LOG_DOMAIN
      60                 :             : #define G_LOG_DOMAIN "gnc.engine.scrub"
      61                 :             : 
      62                 :             : static QofLogModule log_module = G_LOG_DOMAIN;
      63                 :             : static gboolean abort_now = FALSE;
      64                 :             : static gint scrub_depth = 0;
      65                 :             : 
      66                 :             : 
      67                 :             : static Account* xaccScrubUtilityGetOrMakeAccount (Account *root,
      68                 :             :                                                   gnc_commodity* currency,
      69                 :             :                                                   const char* accname,
      70                 :             :                                                   GNCAccountType acctype,
      71                 :             :                                                   gboolean placeholder,
      72                 :             :                                                   gboolean checkname);
      73                 :             : 
      74                 :             : void
      75                 :           0 : gnc_set_abort_scrub (gboolean abort)
      76                 :             : {
      77                 :           0 :     abort_now = abort;
      78                 :           0 : }
      79                 :             : 
      80                 :             : gboolean
      81                 :           0 : gnc_get_abort_scrub (void)
      82                 :             : {
      83                 :           0 :     return abort_now;
      84                 :             : }
      85                 :             : 
      86                 :             : gboolean
      87                 :           0 : gnc_get_ongoing_scrub (void)
      88                 :             : {
      89                 :           0 :     return scrub_depth > 0;
      90                 :             : }
      91                 :             : 
      92                 :             : /* ================================================================ */
      93                 :             : 
      94                 :             : using TransSet = std::unordered_set<Transaction*>;
      95                 :             : 
      96                 :             : static TransSet
      97                 :           0 : get_all_transactions (Account *account, bool descendants)
      98                 :             : {
      99                 :           0 :     TransSet set;
     100                 :           0 :     auto add_transactions = [&set](auto a)
     101                 :           0 :     { gnc_account_foreach_split (a, [&set](auto s){ set.insert (xaccSplitGetParent (s)); }, false); };
     102                 :           0 :     add_transactions (account);
     103                 :           0 :     if (descendants)
     104                 :           0 :         gnc_account_foreach_descendant (account, add_transactions);
     105                 :           0 :     return set;
     106                 :           0 : }
     107                 :             : 
     108                 :             : /* ================================================================ */
     109                 :             : 
     110                 :             : static void
     111                 :        1624 : TransScrubOrphansFast (Transaction *trans, Account *root)
     112                 :             : {
     113                 :        1624 :     g_return_if_fail (trans && trans->common_currency && root);
     114                 :             : 
     115                 :        4904 :     for (GList *node = trans->splits; node; node = node->next)
     116                 :             :     {
     117                 :        3296 :         Split *split = GNC_SPLIT(node->data);
     118                 :        3296 :         if (abort_now) break;
     119                 :             : 
     120                 :        3296 :         if (split->acc) continue;
     121                 :             : 
     122                 :          57 :         DEBUG ("Found an orphan\n");
     123                 :             : 
     124                 :             :         gchar *accname = g_strconcat
     125                 :          57 :             (_("Orphan"), "-", gnc_commodity_get_mnemonic (trans->common_currency),
     126                 :             :              nullptr);
     127                 :             : 
     128                 :             :         Account *orph = xaccScrubUtilityGetOrMakeAccount
     129                 :          57 :             (root, trans->common_currency, accname, ACCT_TYPE_BANK, false, true);
     130                 :             : 
     131                 :          57 :         g_free (accname);
     132                 :          57 :         if (!orph) continue;
     133                 :             : 
     134                 :          57 :         xaccSplitSetAccount(split, orph);
     135                 :             :     }
     136                 :             : }
     137                 :             : 
     138                 :             : static void
     139                 :           0 : AccountScrubOrphans (Account *acc, bool descendants, QofPercentageFunc percentagefunc)
     140                 :             : {
     141                 :           0 :     if (!acc) return;
     142                 :           0 :     scrub_depth++;
     143                 :             : 
     144                 :           0 :     auto transactions = get_all_transactions (acc, descendants);
     145                 :           0 :     auto total_trans = transactions.size();
     146                 :           0 :     const char *message = _("Looking for orphans in transaction: %u of %zu");
     147                 :           0 :     guint current_trans = 0;
     148                 :             : 
     149                 :           0 :     for (auto trans : transactions)
     150                 :             :     {
     151                 :           0 :         if (current_trans % 10 == 0)
     152                 :             :         {
     153                 :           0 :             char *progress_msg = g_strdup_printf (message, current_trans, total_trans);
     154                 :           0 :             (percentagefunc)(progress_msg, (100 * current_trans) / total_trans);
     155                 :           0 :             g_free (progress_msg);
     156                 :           0 :             if (abort_now) break;
     157                 :             :         }
     158                 :             : 
     159                 :           0 :         TransScrubOrphansFast (trans, gnc_account_get_root (acc));
     160                 :           0 :         current_trans++;
     161                 :             :     }
     162                 :           0 :     (percentagefunc)(nullptr, -1.0);
     163                 :           0 :     scrub_depth--;
     164                 :           0 : }
     165                 :             : 
     166                 :             : void
     167                 :           0 : xaccAccountScrubOrphans (Account *acc, QofPercentageFunc percentagefunc)
     168                 :             : {
     169                 :           0 :     AccountScrubOrphans (acc, false, percentagefunc);
     170                 :           0 : }
     171                 :             : 
     172                 :             : void
     173                 :           0 : xaccAccountTreeScrubOrphans (Account *acc, QofPercentageFunc percentagefunc)
     174                 :             : {
     175                 :           0 :     AccountScrubOrphans (acc, true, percentagefunc);
     176                 :           0 : }
     177                 :             : 
     178                 :             : void
     179                 :        1624 : xaccTransScrubOrphans (Transaction *trans)
     180                 :             : {
     181                 :             :     SplitList *node;
     182                 :        1624 :     QofBook *book = nullptr;
     183                 :        1624 :     Account *root = nullptr;
     184                 :             : 
     185                 :        1624 :     if (!trans) return;
     186                 :             : 
     187                 :        1692 :     for (node = trans->splits; node; node = node->next)
     188                 :             :     {
     189                 :        1628 :         Split *split = GNC_SPLIT(node->data);
     190                 :        1628 :         if (abort_now) break;
     191                 :             : 
     192                 :        1628 :         if (split->acc)
     193                 :             :         {
     194                 :        1560 :             TransScrubOrphansFast (trans, gnc_account_get_root(split->acc));
     195                 :        1560 :             return;
     196                 :             :         }
     197                 :             :     }
     198                 :             : 
     199                 :             :     /* If we got to here, then *none* of the splits belonged to an
     200                 :             :      * account.  Not a happy situation.  We should dig an account
     201                 :             :      * out of the book the transaction belongs to.
     202                 :             :      * XXX we should probably *always* to this, instead of the above loop!
     203                 :             :      */
     204                 :          64 :     PINFO ("Free Floating Transaction!");
     205                 :          64 :     book = xaccTransGetBook (trans);
     206                 :          64 :     root = gnc_book_get_root_account (book);
     207                 :          64 :     TransScrubOrphansFast (trans, root);
     208                 :             : }
     209                 :             : 
     210                 :             : /* ================================================================ */
     211                 :             : 
     212                 :             : void
     213                 :          22 : xaccAccountTreeScrubSplits (Account *account)
     214                 :             : {
     215                 :          22 :     if (!account) return;
     216                 :             : 
     217                 :          22 :     xaccAccountScrubSplits (account);
     218                 :          22 :     gnc_account_foreach_descendant(account,
     219                 :             :                                    (AccountCb)xaccAccountScrubSplits, nullptr);
     220                 :             : }
     221                 :             : 
     222                 :             : void
     223                 :         456 : xaccAccountScrubSplits (Account *account)
     224                 :             : {
     225                 :         456 :     scrub_depth++;
     226                 :        2066 :     for (auto s : xaccAccountGetSplits (account))
     227                 :             :     {
     228                 :        1610 :         if (abort_now) break;
     229                 :        1610 :         xaccSplitScrub (s);
     230                 :             :     }
     231                 :         456 :     scrub_depth--;
     232                 :         456 : }
     233                 :             : 
     234                 :             : /* if dry_run is true, this function will analyze the split and
     235                 :             :    return true if the split will be modified during the actual scrub. */
     236                 :             : static bool
     237                 :        8359 : split_scrub_or_dry_run (Split *split, bool dry_run)
     238                 :             : {
     239                 :             :     Account *account;
     240                 :             :     Transaction *trans;
     241                 :             :     gnc_numeric value, amount;
     242                 :             :     gnc_commodity *currency, *acc_commodity;
     243                 :             :     int scu;
     244                 :             : 
     245                 :        8359 :     if (!split) return false;
     246                 :        8359 :     ENTER ("(split=%p)", split);
     247                 :             : 
     248                 :        8359 :     trans = xaccSplitGetParent (split);
     249                 :        8359 :     if (!trans)
     250                 :             :     {
     251                 :           8 :         LEAVE("no trans");
     252                 :           8 :         return false;
     253                 :             :     }
     254                 :             : 
     255                 :        8351 :     account = xaccSplitGetAccount (split);
     256                 :             : 
     257                 :             :     /* If there's no account, this split is an orphan.
     258                 :             :      * We need to fix that first, before proceeding.
     259                 :             :      */
     260                 :        8351 :     if (!account)
     261                 :             :     {
     262                 :         142 :         if (dry_run)
     263                 :          71 :             return true;
     264                 :             :         else
     265                 :          71 :             xaccTransScrubOrphans (trans);
     266                 :          71 :         account = xaccSplitGetAccount (split);
     267                 :             :     }
     268                 :             : 
     269                 :             :     /* Grrr... the register gnc_split_register_load() line 203 of
     270                 :             :      *  src/register/ledger-core/split-register-load.c will create
     271                 :             :      * free-floating bogus transactions. Ignore these for now ...
     272                 :             :      */
     273                 :        8280 :     if (!account)
     274                 :             :     {
     275                 :          16 :         PINFO ("Free Floating Transaction!");
     276                 :          16 :         LEAVE ("no account");
     277                 :          16 :         return false;
     278                 :             :     }
     279                 :             : 
     280                 :             :     /* Split amounts and values should be valid numbers */
     281                 :        8264 :     value = xaccSplitGetValue (split);
     282                 :        8264 :     if (gnc_numeric_check (value))
     283                 :             :     {
     284                 :           0 :         value = gnc_numeric_zero();
     285                 :           0 :         if (dry_run)
     286                 :           0 :             return true;
     287                 :             :         else
     288                 :           0 :             xaccSplitSetValue (split, value);
     289                 :             :     }
     290                 :             : 
     291                 :        8264 :     amount = xaccSplitGetAmount (split);
     292                 :        8264 :     if (gnc_numeric_check (amount))
     293                 :             :     {
     294                 :           0 :         amount = gnc_numeric_zero();
     295                 :           0 :         if (dry_run)
     296                 :           0 :             return true;
     297                 :             :         else
     298                 :           0 :             xaccSplitSetAmount (split, amount);
     299                 :             :     }
     300                 :             : 
     301                 :        8264 :     currency = xaccTransGetCurrency (trans);
     302                 :             : 
     303                 :             :     /* If the account doesn't have a commodity,
     304                 :             :      * we should attempt to fix that first.
     305                 :             :      */
     306                 :        8264 :     acc_commodity = xaccAccountGetCommodity(account);
     307                 :        8264 :     if (!acc_commodity)
     308                 :             :     {
     309                 :        1362 :         if (dry_run)
     310                 :         454 :             return true;
     311                 :             :         else
     312                 :         908 :             xaccAccountScrubCommodity (account);
     313                 :             :     }
     314                 :        7810 :     if (!acc_commodity || !gnc_commodity_equiv(acc_commodity, currency))
     315                 :             :     {
     316                 :        2748 :         LEAVE ("(split=%p) inequiv currency", split);
     317                 :        2748 :         return false;
     318                 :             :     }
     319                 :             : 
     320                 :        5062 :     scu = MIN (xaccAccountGetCommoditySCU (account),
     321                 :             :                gnc_commodity_get_fraction (currency));
     322                 :             : 
     323                 :        5062 :     if (gnc_numeric_same (amount, value, scu, GNC_HOW_RND_ROUND_HALF_UP))
     324                 :             :     {
     325                 :        4995 :         LEAVE("(split=%p) different values", split);
     326                 :        4995 :         return false;
     327                 :             :     }
     328                 :             : 
     329                 :          67 :     if (dry_run)
     330                 :          30 :         return true;
     331                 :             : 
     332                 :             :     /*
     333                 :             :      * This will be hit every time you answer yes to the dialog "The
     334                 :             :      * current transaction has changed. Would you like to record it.
     335                 :             :      */
     336                 :          37 :     PINFO ("Adjusted split with mismatched values, desc=\"%s\" memo=\"%s\""
     337                 :             :            " old amount %s %s, new amount %s",
     338                 :             :            trans->description, split->memo,
     339                 :             :            gnc_num_dbg_to_string (xaccSplitGetAmount(split)),
     340                 :             :            gnc_commodity_get_mnemonic (currency),
     341                 :             :            gnc_num_dbg_to_string (xaccSplitGetValue(split)));
     342                 :             : 
     343                 :          37 :     xaccTransBeginEdit (trans);
     344                 :          37 :     xaccSplitSetAmount (split, value);
     345                 :          37 :     xaccTransCommitEdit (trans);
     346                 :          37 :     LEAVE ("(split=%p)", split);
     347                 :          37 :     return true;
     348                 :             : }
     349                 :             : 
     350                 :             : /* ================================================================ */
     351                 :             : 
     352                 :             : 
     353                 :             : static void
     354                 :           0 : AccountScrubImbalance (Account *acc, bool descendants,
     355                 :             :                        QofPercentageFunc percentagefunc)
     356                 :             : {
     357                 :           0 :     const char *message = _("Looking for imbalances in transaction date %s: %u of %zu");
     358                 :             : 
     359                 :           0 :     if (!acc) return;
     360                 :             : 
     361                 :           0 :     QofBook *book = qof_session_get_book (gnc_get_current_session ());
     362                 :           0 :     Account *root = gnc_book_get_root_account (book);
     363                 :           0 :     auto transactions = get_all_transactions (acc, descendants);
     364                 :           0 :     auto count = transactions.size();
     365                 :           0 :     auto curr_trans = 0;
     366                 :             : 
     367                 :           0 :     scrub_depth++;
     368                 :           0 :     for (auto trans : transactions)
     369                 :             :     {
     370                 :           0 :         if (abort_now) break;
     371                 :             : 
     372                 :           0 :         PINFO("Start processing transaction %d of %zu", curr_trans + 1, count);
     373                 :             : 
     374                 :           0 :         if (curr_trans % 10 == 0)
     375                 :             :         {
     376                 :           0 :             char *date = qof_print_date (xaccTransGetDate (trans));
     377                 :           0 :             char *progress_msg = g_strdup_printf (message, date, curr_trans, count);
     378                 :           0 :             (percentagefunc)(progress_msg, (100 * curr_trans) / count);
     379                 :           0 :             g_free (progress_msg);
     380                 :           0 :             g_free (date);
     381                 :             :         }
     382                 :             : 
     383                 :           0 :         TransScrubOrphansFast (trans, root);
     384                 :           0 :         xaccTransScrubCurrency(trans);
     385                 :           0 :         xaccTransScrubImbalance (trans, root, nullptr);
     386                 :             : 
     387                 :           0 :         PINFO("Finished processing transaction %d of %zu", curr_trans + 1, count);
     388                 :           0 :         curr_trans++;
     389                 :             :     }
     390                 :           0 :     (percentagefunc)(nullptr, -1.0);
     391                 :           0 :     scrub_depth--;
     392                 :           0 : }
     393                 :             : 
     394                 :             : void
     395                 :        3008 : xaccTransScrubSplits (Transaction *trans)
     396                 :             : {
     397                 :        3008 :     if (!trans) return;
     398                 :             : 
     399                 :        3008 :     gnc_commodity *currency = xaccTransGetCurrency (trans);
     400                 :        3008 :     if (!currency)
     401                 :         166 :         PERR ("Transaction doesn't have a currency!");
     402                 :             : 
     403                 :        3008 :     bool must_scrub = false;
     404                 :             : 
     405                 :        8625 :     for (GList *n = xaccTransGetSplitList (trans); !must_scrub && n; n = g_list_next (n))
     406                 :        5617 :         if (split_scrub_or_dry_run (GNC_SPLIT(n->data), true))
     407                 :         555 :             must_scrub = true;
     408                 :             : 
     409                 :        3008 :     if (!must_scrub)
     410                 :        2453 :         return;
     411                 :             : 
     412                 :         555 :     xaccTransBeginEdit(trans);
     413                 :             :     /* The split scrub expects the transaction to have a currency! */
     414                 :             : 
     415                 :        1641 :     for (GList *n = xaccTransGetSplitList (trans); n; n = g_list_next (n))
     416                 :        1086 :         xaccSplitScrub (GNC_SPLIT(n->data));
     417                 :             : 
     418                 :         555 :     xaccTransCommitEdit(trans);
     419                 :             : }
     420                 :             : 
     421                 :             : /* ================================================================ */
     422                 :             : 
     423                 :             : void
     424                 :        2742 : xaccSplitScrub (Split *split)
     425                 :             : {
     426                 :        2742 :     split_scrub_or_dry_run (split, false);
     427                 :        2742 : }
     428                 :             : 
     429                 :             : /* ================================================================ */
     430                 :             : 
     431                 :             : 
     432                 :             : void
     433                 :           0 : xaccAccountTreeScrubImbalance (Account *acc, QofPercentageFunc percentagefunc)
     434                 :             : {
     435                 :           0 :     AccountScrubImbalance (acc, true, percentagefunc);
     436                 :           0 : }
     437                 :             : 
     438                 :             : void
     439                 :           0 : xaccAccountScrubImbalance (Account *acc, QofPercentageFunc percentagefunc)
     440                 :             : {
     441                 :           0 :     AccountScrubImbalance (acc, false, percentagefunc);
     442                 :           0 : }
     443                 :             : 
     444                 :             : static Split *
     445                 :          38 : get_balance_split (Transaction *trans, Account *root, Account *account,
     446                 :             :                    gnc_commodity *commodity)
     447                 :             : {
     448                 :             :     Split *balance_split;
     449                 :             :     gchar *accname;
     450                 :             : 
     451                 :          38 :     if (!account ||
     452                 :           0 :         !gnc_commodity_equiv (commodity, xaccAccountGetCommodity(account)))
     453                 :             :     {
     454                 :          38 :         if (!root)
     455                 :             :         {
     456                 :          38 :             root = gnc_book_get_root_account (xaccTransGetBook (trans));
     457                 :          38 :             if (nullptr == root)
     458                 :             :             {
     459                 :             :                 /* This can't occur, things should be in books */
     460                 :           0 :                 PERR ("Bad data corruption, no root account in book");
     461                 :           0 :                 return nullptr;
     462                 :             :             }
     463                 :             :         }
     464                 :          38 :         accname = g_strconcat (_("Imbalance"), "-",
     465                 :             :                                gnc_commodity_get_mnemonic (commodity), nullptr);
     466                 :          38 :         account = xaccScrubUtilityGetOrMakeAccount (root, commodity,
     467                 :             :                                                     accname, ACCT_TYPE_BANK,
     468                 :             :                                                     FALSE, TRUE);
     469                 :          38 :         g_free (accname);
     470                 :          38 :         if (!account)
     471                 :             :         {
     472                 :           0 :             PERR ("Can't get balancing account");
     473                 :           0 :             return nullptr;
     474                 :             :         }
     475                 :             :     }
     476                 :             : 
     477                 :          38 :     balance_split = xaccTransFindSplitByAccount(trans, account);
     478                 :             : 
     479                 :             :     /* Put split into account before setting split value */
     480                 :          38 :     if (!balance_split)
     481                 :             :     {
     482                 :          27 :         balance_split = xaccMallocSplit (qof_instance_get_book(trans));
     483                 :             : 
     484                 :          27 :         xaccTransBeginEdit (trans);
     485                 :          27 :         xaccSplitSetParent(balance_split, trans);
     486                 :          27 :         xaccSplitSetAccount(balance_split, account);
     487                 :          27 :         xaccTransCommitEdit (trans);
     488                 :             :     }
     489                 :             : 
     490                 :          38 :     return balance_split;
     491                 :             : }
     492                 :             : 
     493                 :             : static gnc_commodity*
     494                 :          79 : find_root_currency(void)
     495                 :             : {
     496                 :          79 :     QofSession *sess = gnc_get_current_session ();
     497                 :          79 :     Account *root = gnc_book_get_root_account (qof_session_get_book (sess));
     498                 :          79 :     gnc_commodity *root_currency = xaccAccountGetCommodity (root);
     499                 :             : 
     500                 :             :     /* Some older books may not have a currency set on the root
     501                 :             :      * account. In that case find the first top-level INCOME account
     502                 :             :      * and use its currency. */
     503                 :          79 :     if (!root_currency)
     504                 :             :     {
     505                 :          79 :          GList *children = gnc_account_get_children (root);
     506                 :          79 :          for (GList *node = children; node && !root_currency;
     507                 :           0 :               node = g_list_next (node))
     508                 :             :          {
     509                 :           0 :               Account *child = GNC_ACCOUNT (node->data);
     510                 :           0 :               if (xaccAccountGetType (child) == ACCT_TYPE_INCOME)
     511                 :           0 :                    root_currency = xaccAccountGetCommodity (child);
     512                 :             :          }
     513                 :          79 :          g_list_free (children);
     514                 :             :     }
     515                 :          79 :     return root_currency;
     516                 :             : }
     517                 :             : 
     518                 :             : /* Get the trading split for a given commodity, creating it (and the
     519                 :             :    necessary parent accounts) if it doesn't exist. */
     520                 :             : static Split *
     521                 :           0 : get_trading_split (Transaction *trans, Account *base,
     522                 :             :                    gnc_commodity *commodity)
     523                 :             : {
     524                 :             :     Split *balance_split;
     525                 :             :     Account *trading_account;
     526                 :             :     Account *ns_account;
     527                 :             :     Account *account;
     528                 :           0 :     Account* root = gnc_book_get_root_account (xaccTransGetBook (trans));
     529                 :             : 
     530                 :           0 :     trading_account = xaccScrubUtilityGetOrMakeAccount (root,
     531                 :             :                                                         nullptr,
     532                 :           0 :                                                         _("Trading"),
     533                 :             :                                                         ACCT_TYPE_TRADING,
     534                 :             :                                                         TRUE, FALSE);
     535                 :           0 :     if (!trading_account)
     536                 :             :     {
     537                 :           0 :         PERR ("Can't get trading account");
     538                 :           0 :         return nullptr;
     539                 :             :     }
     540                 :             : 
     541                 :           0 :     ns_account = xaccScrubUtilityGetOrMakeAccount (trading_account,
     542                 :             :                                                    nullptr,
     543                 :             :                                                    gnc_commodity_get_namespace(commodity),
     544                 :             :                                                    ACCT_TYPE_TRADING,
     545                 :             :                                                    TRUE, TRUE);
     546                 :           0 :     if (!ns_account)
     547                 :             :     {
     548                 :           0 :         PERR ("Can't get namespace account");
     549                 :           0 :         return nullptr;
     550                 :             :     }
     551                 :             : 
     552                 :           0 :     account = xaccScrubUtilityGetOrMakeAccount (ns_account, commodity,
     553                 :             :                                                 gnc_commodity_get_mnemonic(commodity),
     554                 :             :                                                 ACCT_TYPE_TRADING,
     555                 :             :                                                 FALSE, FALSE);
     556                 :           0 :     if (!account)
     557                 :             :     {
     558                 :           0 :         PERR ("Can't get commodity account");
     559                 :           0 :         return nullptr;
     560                 :             :     }
     561                 :             : 
     562                 :             : 
     563                 :           0 :     balance_split = xaccTransFindSplitByAccount(trans, account);
     564                 :             : 
     565                 :             :     /* Put split into account before setting split value */
     566                 :           0 :     if (!balance_split)
     567                 :             :     {
     568                 :           0 :         balance_split = xaccMallocSplit (qof_instance_get_book(trans));
     569                 :           0 :         xaccDisableDataScrubbing();
     570                 :             : 
     571                 :           0 :         xaccTransBeginEdit (trans);
     572                 :           0 :         xaccSplitSetParent(balance_split, trans);
     573                 :           0 :         xaccSplitSetAccount(balance_split, account);
     574                 :           0 :         xaccTransCommitEdit (trans);
     575                 :           0 :         xaccEnableDataScrubbing();
     576                 :             :     }
     577                 :             : 
     578                 :           0 :     return balance_split;
     579                 :             : }
     580                 :             : 
     581                 :             : static void
     582                 :          38 : add_balance_split (Transaction *trans, gnc_numeric imbalance,
     583                 :             :                    Account *root, Account *account)
     584                 :             : {
     585                 :             :     const gnc_commodity *commodity;
     586                 :             :     gnc_numeric old_value, new_value;
     587                 :             :     Split *balance_split;
     588                 :          38 :     gnc_commodity *currency = xaccTransGetCurrency (trans);
     589                 :             : 
     590                 :          38 :     balance_split = get_balance_split(trans, root, account, currency);
     591                 :          38 :     if (!balance_split)
     592                 :             :     {
     593                 :             :         /* Error already logged */
     594                 :           0 :         LEAVE("");
     595                 :           0 :         return;
     596                 :             :     }
     597                 :          38 :     account = xaccSplitGetAccount(balance_split);
     598                 :             : 
     599                 :          38 :     xaccTransBeginEdit (trans);
     600                 :             : 
     601                 :          38 :     old_value = xaccSplitGetValue (balance_split);
     602                 :             : 
     603                 :             :     /* Note: We have to round for the commodity's fraction, NOT any
     604                 :             :      * already existing denominator (bug #104343), because either one
     605                 :             :      * of the denominators might already be reduced.  */
     606                 :          38 :     new_value = gnc_numeric_sub (old_value, imbalance,
     607                 :          38 :                                  gnc_commodity_get_fraction(currency),
     608                 :             :                                  GNC_HOW_RND_ROUND_HALF_UP);
     609                 :             : 
     610                 :          38 :     xaccSplitSetValue (balance_split, new_value);
     611                 :             : 
     612                 :          38 :     commodity = xaccAccountGetCommodity (account);
     613                 :          38 :     if (gnc_commodity_equiv (currency, commodity))
     614                 :             :     {
     615                 :          38 :         xaccSplitSetAmount (balance_split, new_value);
     616                 :             :     }
     617                 :             : 
     618                 :          38 :     xaccSplitScrub (balance_split);
     619                 :          38 :     xaccTransCommitEdit (trans);
     620                 :             : }
     621                 :             : 
     622                 :             : /* Balance a transaction without trading accounts. */
     623                 :             : static void
     624                 :          38 : gnc_transaction_balance_no_trading (Transaction *trans, Account *root,
     625                 :             :                                     Account *account)
     626                 :             : {
     627                 :          38 :     gnc_numeric imbalance  = xaccTransGetImbalanceValue (trans);
     628                 :             : 
     629                 :             :     /* Make the value sum to zero */
     630                 :          38 :     if (! gnc_numeric_zero_p (imbalance))
     631                 :             :     {
     632                 :          38 :         PINFO ("Value unbalanced transaction");
     633                 :             : 
     634                 :          38 :         add_balance_split (trans, imbalance, root, account);
     635                 :             :     }
     636                 :             : 
     637                 :          38 : }
     638                 :             : 
     639                 :             : static gnc_numeric
     640                 :           0 : gnc_transaction_get_commodity_imbalance (Transaction *trans,
     641                 :             :                                          gnc_commodity *commodity)
     642                 :             : {
     643                 :             :     /* Find the value imbalance in this commodity */
     644                 :           0 :     gnc_numeric val_imbalance = gnc_numeric_zero();
     645                 :           0 :     GList *splits = nullptr;
     646                 :           0 :     for (splits = trans->splits; splits; splits = splits->next)
     647                 :             :     {
     648                 :           0 :         Split *split = GNC_SPLIT(splits->data);
     649                 :             :         gnc_commodity *split_commodity =
     650                 :           0 :             xaccAccountGetCommodity(xaccSplitGetAccount(split));
     651                 :           0 :         if (xaccTransStillHasSplit (trans, split) &&
     652                 :           0 :             gnc_commodity_equal (commodity, split_commodity))
     653                 :           0 :             val_imbalance = gnc_numeric_add (val_imbalance,
     654                 :             :                                              xaccSplitGetValue (split),
     655                 :             :                                              GNC_DENOM_AUTO,
     656                 :             :                                              GNC_HOW_DENOM_EXACT);
     657                 :             :     }
     658                 :           0 :     return val_imbalance;
     659                 :             : }
     660                 :             : 
     661                 :             : /* GFunc wrapper for xaccSplitDestroy */
     662                 :             : static void
     663                 :           0 : destroy_split (void* ptr)
     664                 :             : {
     665                 :           0 :     Split *split = GNC_SPLIT (ptr);
     666                 :           0 :     if (split)
     667                 :           0 :         xaccSplitDestroy (split);
     668                 :           0 : }
     669                 :             : 
     670                 :             : /* Balancing transactions with trading accounts works best when
     671                 :             :  * starting with no trading splits.
     672                 :             :  */
     673                 :             : static void
     674                 :           0 : xaccTransClearTradingSplits (Transaction *trans)
     675                 :             : {
     676                 :           0 :     GList *trading_splits = nullptr;
     677                 :             : 
     678                 :           0 :     for (GList* node = trans->splits; node; node = node->next)
     679                 :             :     {
     680                 :           0 :          Split* split = GNC_SPLIT(node->data);
     681                 :           0 :          Account* acc = nullptr;
     682                 :           0 :          if (!split)
     683                 :           0 :               continue;
     684                 :           0 :          acc = xaccSplitGetAccount(split);
     685                 :           0 :          if (acc && xaccAccountGetType(acc) == ACCT_TYPE_TRADING)
     686                 :           0 :             trading_splits = g_list_prepend (trading_splits, node->data);
     687                 :             :     }
     688                 :             : 
     689                 :           0 :     if (!trading_splits)
     690                 :           0 :         return;
     691                 :             : 
     692                 :           0 :     xaccTransBeginEdit (trans);
     693                 :             :     /* destroy_splits doesn't actually free the splits but this gets
     694                 :             :      * the list itself freed.
     695                 :             :      */
     696                 :           0 :     g_list_free_full (trading_splits, destroy_split);
     697                 :           0 :     xaccTransCommitEdit (trans);
     698                 :             : }
     699                 :             : 
     700                 :             : static void
     701                 :           0 : gnc_transaction_balance_trading (Transaction *trans, Account *root)
     702                 :             : {
     703                 :             :     MonetaryList *imbal_list;
     704                 :             :     MonetaryList *imbalance_commod;
     705                 :           0 :     Split *balance_split = nullptr;
     706                 :             : 
     707                 :             :     /* If the transaction is balanced, nothing more to do */
     708                 :           0 :     imbal_list = xaccTransGetImbalance (trans);
     709                 :           0 :     if (!imbal_list)
     710                 :             :     {
     711                 :           0 :         LEAVE("transaction is balanced");
     712                 :           0 :         return;
     713                 :             :     }
     714                 :             : 
     715                 :           0 :     PINFO ("Currency unbalanced transaction");
     716                 :             : 
     717                 :           0 :     for (imbalance_commod = imbal_list; imbalance_commod;
     718                 :           0 :          imbalance_commod = imbalance_commod->next)
     719                 :             :     {
     720                 :           0 :         auto imbal_mon = static_cast<gnc_monetary*>(imbalance_commod->data);
     721                 :             :         gnc_commodity *commodity;
     722                 :             :         gnc_numeric old_amount, new_amount;
     723                 :           0 :         const gnc_commodity *txn_curr = xaccTransGetCurrency (trans);
     724                 :             : 
     725                 :           0 :         commodity = gnc_monetary_commodity (*imbal_mon);
     726                 :             : 
     727                 :           0 :         balance_split = get_trading_split(trans, root, commodity);
     728                 :           0 :         if (!balance_split)
     729                 :             :         {
     730                 :             :             /* Error already logged */
     731                 :           0 :             gnc_monetary_list_free(imbal_list);
     732                 :           0 :             LEAVE("");
     733                 :           0 :             return;
     734                 :             :         }
     735                 :             : 
     736                 :           0 :         xaccTransBeginEdit (trans);
     737                 :             : 
     738                 :           0 :         old_amount = xaccSplitGetAmount (balance_split);
     739                 :           0 :         new_amount = gnc_numeric_sub (old_amount, gnc_monetary_value(*imbal_mon),
     740                 :           0 :                                       gnc_commodity_get_fraction(commodity),
     741                 :             :                                       GNC_HOW_RND_ROUND_HALF_UP);
     742                 :             : 
     743                 :           0 :         xaccSplitSetAmount (balance_split, new_amount);
     744                 :             : 
     745                 :           0 :         if (gnc_commodity_equal (txn_curr, commodity))
     746                 :             :         {
     747                 :             :             /* Imbalance commodity is the transaction currency, value in the
     748                 :             :                split must be the same as the amount */
     749                 :           0 :             xaccSplitSetValue (balance_split, new_amount);
     750                 :             :         }
     751                 :             :         else
     752                 :             :         {
     753                 :           0 :             gnc_numeric val_imbalance = gnc_transaction_get_commodity_imbalance (trans,            commodity);
     754                 :             : 
     755                 :           0 :             gnc_numeric old_value = xaccSplitGetValue (balance_split);
     756                 :           0 :             gnc_numeric new_value = gnc_numeric_sub (old_value, val_imbalance,
     757                 :           0 :                                          gnc_commodity_get_fraction(txn_curr),
     758                 :             :                                          GNC_HOW_RND_ROUND_HALF_UP);
     759                 :             : 
     760                 :           0 :             xaccSplitSetValue (balance_split, new_value);
     761                 :             :         }
     762                 :             : 
     763                 :           0 :         xaccSplitScrub (balance_split);
     764                 :           0 :         xaccTransCommitEdit (trans);
     765                 :             :     }
     766                 :             : 
     767                 :           0 :     gnc_monetary_list_free(imbal_list);
     768                 :             : }
     769                 :             : 
     770                 :             : /** Balance the transaction by adding more trading splits. This shouldn't
     771                 :             :  * ordinarily be necessary.
     772                 :             :  * @param trans the transaction to balance
     773                 :             :  * @param root the root account
     774                 :             :  */
     775                 :             : static void
     776                 :           0 : gnc_transaction_balance_trading_more_splits (Transaction *trans, Account *root)
     777                 :             : {
     778                 :             :     /* Copy the split list so we don't see the splits we're adding */
     779                 :           0 :     GList *splits_dup = g_list_copy(trans->splits), *splits = nullptr;
     780                 :           0 :     const gnc_commodity  *txn_curr = xaccTransGetCurrency (trans);
     781                 :           0 :     for (splits = splits_dup; splits; splits = splits->next)
     782                 :             :     {
     783                 :           0 :         Split *split = GNC_SPLIT(splits->data);
     784                 :           0 :         if (! xaccTransStillHasSplit(trans, split)) continue;
     785                 :           0 :         if (!gnc_numeric_zero_p(xaccSplitGetValue(split)) &&
     786                 :           0 :             gnc_numeric_zero_p(xaccSplitGetAmount(split)))
     787                 :             :         {
     788                 :             :             gnc_commodity *commodity;
     789                 :             :             gnc_numeric old_value, new_value;
     790                 :             :             Split *balance_split;
     791                 :             : 
     792                 :           0 :             commodity = xaccAccountGetCommodity(xaccSplitGetAccount(split));
     793                 :           0 :             if (!commodity)
     794                 :             :             {
     795                 :           0 :                 PERR("Split has no commodity");
     796                 :           0 :                 continue;
     797                 :             :             }
     798                 :           0 :             balance_split = get_trading_split(trans, root, commodity);
     799                 :           0 :             if (!balance_split)
     800                 :             :             {
     801                 :             :                 /* Error already logged */
     802                 :           0 :                 LEAVE("");
     803                 :           0 :                 return;
     804                 :             :             }
     805                 :           0 :             xaccTransBeginEdit (trans);
     806                 :             : 
     807                 :           0 :             old_value = xaccSplitGetValue (balance_split);
     808                 :           0 :             new_value = gnc_numeric_sub (old_value, xaccSplitGetValue(split),
     809                 :           0 :                                          gnc_commodity_get_fraction(txn_curr),
     810                 :             :                                          GNC_HOW_RND_ROUND_HALF_UP);
     811                 :           0 :             xaccSplitSetValue (balance_split, new_value);
     812                 :             : 
     813                 :             :             /* Don't change the balance split's amount since the amount
     814                 :             :                is zero in the split we're working on */
     815                 :             : 
     816                 :           0 :             xaccSplitScrub (balance_split);
     817                 :           0 :             xaccTransCommitEdit (trans);
     818                 :             :         }
     819                 :             :     }
     820                 :             : 
     821                 :           0 :     g_list_free(splits_dup);
     822                 :             : }
     823                 :             : 
     824                 :             : /** Correct transaction imbalances.
     825                 :             :  * @param trans The Transaction
     826                 :             :  * @param root The (hidden) root account, for the book default currency.
     827                 :             :  * @param account The account whose currency in which to balance.
     828                 :             :  */
     829                 :             : 
     830                 :             : void
     831                 :        3008 : xaccTransScrubImbalance (Transaction *trans, Account *root,
     832                 :             :                          Account *account)
     833                 :             : {
     834                 :             :     gnc_numeric imbalance;
     835                 :             : 
     836                 :        6016 :     if (!trans) return;
     837                 :             : 
     838                 :        3008 :     ENTER ("()");
     839                 :             : 
     840                 :             :     /* Must look for orphan splits even if there is no imbalance. */
     841                 :        3008 :     xaccTransScrubSplits (trans);
     842                 :             : 
     843                 :             :     /* Return immediately if things are balanced. */
     844                 :        3008 :     if (xaccTransIsBalanced (trans))
     845                 :             :     {
     846                 :        2970 :         LEAVE ("transaction is balanced");
     847                 :        2970 :         return;
     848                 :             :     }
     849                 :             : 
     850                 :          38 :     if (! xaccTransUseTradingAccounts (trans))
     851                 :             :     {
     852                 :          38 :         gnc_transaction_balance_no_trading (trans, root, account);
     853                 :          38 :         LEAVE ("transaction balanced, no managed trading accounts");
     854                 :          38 :         return;
     855                 :             :     }
     856                 :             : 
     857                 :           0 :     xaccTransClearTradingSplits (trans);
     858                 :           0 :     imbalance = xaccTransGetImbalanceValue (trans);
     859                 :           0 :     if (! gnc_numeric_zero_p (imbalance))
     860                 :             :     {
     861                 :           0 :         PINFO ("Value unbalanced transaction");
     862                 :             : 
     863                 :           0 :         add_balance_split (trans, imbalance, root, account);
     864                 :             :     }
     865                 :             : 
     866                 :           0 :     gnc_transaction_balance_trading (trans, root);
     867                 :           0 :     if (gnc_numeric_zero_p(xaccTransGetImbalanceValue(trans)))
     868                 :             :     {
     869                 :           0 :         LEAVE ("()");
     870                 :           0 :         return;
     871                 :             :     }
     872                 :             :     /* If the transaction is still not balanced, it's probably because there
     873                 :             :        are splits with zero amount and non-zero value.  These are usually
     874                 :             :        realized gain/loss splits.  Add a reversing split for each of them to
     875                 :             :        balance the value. */
     876                 :             : 
     877                 :           0 :     gnc_transaction_balance_trading_more_splits (trans, root);
     878                 :           0 :     if (!gnc_numeric_zero_p(xaccTransGetImbalanceValue(trans)))
     879                 :           0 :         PERR("Balancing currencies unbalanced value");
     880                 :             : 
     881                 :             : }
     882                 :             : 
     883                 :             : /* ================================================================ */
     884                 :             : /* The xaccTransFindCommonCurrency () method returns
     885                 :             :  *    a gnc_commodity indicating a currency denomination that all
     886                 :             :  *    of the splits in this transaction have in common, using the
     887                 :             :  *    old/obsolete currency/security fields of the split accounts.
     888                 :             :  */
     889                 :             : 
     890                 :             : static gnc_commodity *
     891                 :           0 : FindCommonExclSCurrency (SplitList *splits,
     892                 :             :                          gnc_commodity * ra, gnc_commodity * rb,
     893                 :             :                          Split *excl_split)
     894                 :             : {
     895                 :             :     GList *node;
     896                 :             : 
     897                 :           0 :     if (!splits) return nullptr;
     898                 :             : 
     899                 :           0 :     for (node = splits; node; node = node->next)
     900                 :             :     {
     901                 :           0 :         Split *s = GNC_SPLIT(node->data);
     902                 :             :         gnc_commodity * sa, * sb;
     903                 :             : 
     904                 :           0 :         if (s == excl_split) continue;
     905                 :             : 
     906                 :           0 :         g_return_val_if_fail (s->acc, nullptr);
     907                 :             : 
     908                 :           0 :         sa = DxaccAccountGetCurrency (s->acc);
     909                 :           0 :         sb = xaccAccountGetCommodity (s->acc);
     910                 :             : 
     911                 :           0 :         if (ra && rb)
     912                 :             :         {
     913                 :           0 :             int aa = !gnc_commodity_equiv(ra, sa);
     914                 :           0 :             int ab = !gnc_commodity_equiv(ra, sb);
     915                 :           0 :             int ba = !gnc_commodity_equiv(rb, sa);
     916                 :           0 :             int bb = !gnc_commodity_equiv(rb, sb);
     917                 :             : 
     918                 :           0 :             if ( (!aa) && bb) rb = nullptr;
     919                 :           0 :             else if ( (!ab) && ba) rb = nullptr;
     920                 :           0 :             else if ( (!ba) && ab) ra = nullptr;
     921                 :           0 :             else if ( (!bb) && aa) ra = nullptr;
     922                 :           0 :             else if ( aa && bb && ab && ba )
     923                 :             :             {
     924                 :           0 :                 ra = nullptr;
     925                 :           0 :                 rb = nullptr;
     926                 :             :             }
     927                 :             : 
     928                 :           0 :             if (!ra)
     929                 :             :             {
     930                 :           0 :                 ra = rb;
     931                 :           0 :                 rb = nullptr;
     932                 :             :             }
     933                 :           0 :         }
     934                 :           0 :         else if (ra && !rb)
     935                 :             :         {
     936                 :           0 :             int aa = !gnc_commodity_equiv(ra, sa);
     937                 :           0 :             int ab = !gnc_commodity_equiv(ra, sb);
     938                 :           0 :             if ( aa && ab ) ra = nullptr;
     939                 :           0 :         }
     940                 :           0 :         else if (!ra && rb)
     941                 :             :         {
     942                 :           0 :             int aa = !gnc_commodity_equiv(rb, sa);
     943                 :           0 :             int ab = !gnc_commodity_equiv(rb, sb);
     944                 :           0 :             ra = ( aa && ab ) ? nullptr : rb;
     945                 :             :         }
     946                 :             : 
     947                 :           0 :         if ((!ra) && (!rb)) return nullptr;
     948                 :             :     }
     949                 :             : 
     950                 :           0 :     return (ra);
     951                 :             : }
     952                 :             : 
     953                 :             : /* This is the wrapper for those calls (i.e. the older ones) which
     954                 :             :  * don't exclude one split from the splitlist when looking for a
     955                 :             :  * common currency.
     956                 :             :  */
     957                 :             : static gnc_commodity *
     958                 :           0 : FindCommonCurrency (GList *splits, gnc_commodity * ra, gnc_commodity * rb)
     959                 :             : {
     960                 :           0 :     return FindCommonExclSCurrency(splits, ra, rb, nullptr);
     961                 :             : }
     962                 :             : 
     963                 :             : static gnc_commodity *
     964                 :           0 : xaccTransFindOldCommonCurrency (Transaction *trans, QofBook *book)
     965                 :             : {
     966                 :             :     gnc_commodity *ra, *rb, *retval;
     967                 :             :     Split *split;
     968                 :             : 
     969                 :           0 :     if (!trans) return nullptr;
     970                 :             : 
     971                 :           0 :     if (trans->splits == nullptr) return nullptr;
     972                 :             : 
     973                 :           0 :     g_return_val_if_fail (book, nullptr);
     974                 :             : 
     975                 :           0 :     split = GNC_SPLIT(trans->splits->data);
     976                 :             : 
     977                 :           0 :     if (!split || nullptr == split->acc) return nullptr;
     978                 :             : 
     979                 :           0 :     ra = DxaccAccountGetCurrency (split->acc);
     980                 :           0 :     rb = xaccAccountGetCommodity (split->acc);
     981                 :             : 
     982                 :           0 :     retval = FindCommonCurrency (trans->splits, ra, rb);
     983                 :             : 
     984                 :           0 :     if (retval && !gnc_commodity_is_currency(retval))
     985                 :           0 :         retval = nullptr;
     986                 :             : 
     987                 :           0 :     return retval;
     988                 :             : }
     989                 :             : 
     990                 :             : /* Test the currency of the splits and find the most common and return
     991                 :             :  * it, or nullptr if there is no currency more common than the
     992                 :             :  * others -- or none at all.
     993                 :             :  */
     994                 :             : typedef struct
     995                 :             : {
     996                 :             :     gnc_commodity *commodity;
     997                 :             :     unsigned int count;
     998                 :             : } CommodityCount;
     999                 :             : 
    1000                 :             : static gint
    1001                 :         202 : commodity_equal (gconstpointer a, gconstpointer b)
    1002                 :             : {
    1003                 :         202 :     CommodityCount *cc = (CommodityCount*)a;
    1004                 :         202 :     gnc_commodity *com = (gnc_commodity*)b;
    1005                 :         404 :     if ( cc == nullptr || cc->commodity == nullptr ||
    1006                 :         202 :          !GNC_IS_COMMODITY( cc->commodity ) ) return -1;
    1007                 :         202 :     if ( com == nullptr || !GNC_IS_COMMODITY( com ) ) return 1;
    1008                 :         202 :     if ( gnc_commodity_equal(cc->commodity, com) )
    1009                 :         202 :         return 0;
    1010                 :           0 :     return 1;
    1011                 :             : }
    1012                 :             : 
    1013                 :             : static gint
    1014                 :           0 : commodity_compare( gconstpointer a, gconstpointer b)
    1015                 :             : {
    1016                 :           0 :     CommodityCount *ca = (CommodityCount*)a, *cb = (CommodityCount*)b;
    1017                 :           0 :     if (ca == nullptr || ca->commodity == nullptr ||
    1018                 :           0 :         !GNC_IS_COMMODITY( ca->commodity ) )
    1019                 :             :     {
    1020                 :           0 :         if (cb == nullptr || cb->commodity == nullptr ||
    1021                 :           0 :             !GNC_IS_COMMODITY( cb->commodity ) )
    1022                 :           0 :             return 0;
    1023                 :           0 :         return -1;
    1024                 :             :     }
    1025                 :           0 :     if (cb == nullptr || cb->commodity == nullptr ||
    1026                 :           0 :         !GNC_IS_COMMODITY( cb->commodity ) )
    1027                 :           0 :         return 1;
    1028                 :           0 :     if (ca->count == cb->count)
    1029                 :           0 :         return 0;
    1030                 :           0 :     return ca->count > cb->count ? 1 : -1;
    1031                 :             : }
    1032                 :             : 
    1033                 :             : /* Find the commodities in the account of each of the splits of a
    1034                 :             :  * transaction, and rank them by how many splits in which they
    1035                 :             :  * occur. Commodities which are currencies count more than those which
    1036                 :             :  * aren't, because for simple buy and sell transactions it makes
    1037                 :             :  * slightly more sense for the transaction commodity to be the
    1038                 :             :  * currency -- to the extent that it makes sense for a transaction to
    1039                 :             :  * have a currency at all. jralls, 2010-11-02 */
    1040                 :             : 
    1041                 :             : static gnc_commodity *
    1042                 :         198 : xaccTransFindCommonCurrency (Transaction *trans, QofBook *book)
    1043                 :             : {
    1044                 :             :     gnc_commodity *com_scratch;
    1045                 :         198 :     GList *node = nullptr;
    1046                 :         198 :     GSList *comlist = nullptr, *found = nullptr;
    1047                 :             : 
    1048                 :         198 :     if (!trans) return nullptr;
    1049                 :             : 
    1050                 :         198 :     if (trans->splits == nullptr) return nullptr;
    1051                 :             : 
    1052                 :         198 :     g_return_val_if_fail (book, nullptr);
    1053                 :             : 
    1054                 :             :     /* Find the most commonly used currency among the splits.  If a given split
    1055                 :             :        is in a non-currency commodity, then look for an ancestor account in a
    1056                 :             :        currency, but prefer currencies used directly in splits.  Ignore trading
    1057                 :             :        account splits in this whole process, they don't add any value to this algorithm. */
    1058                 :         601 :     for (node = trans->splits; node; node = node->next)
    1059                 :             :     {
    1060                 :         403 :         Split *s = GNC_SPLIT(node->data);
    1061                 :             :         unsigned int curr_weight;
    1062                 :             : 
    1063                 :         403 :         if (s == nullptr || s->acc == nullptr) continue;
    1064                 :         403 :         if (xaccAccountGetType(s->acc) == ACCT_TYPE_TRADING) continue;
    1065                 :         403 :         com_scratch = xaccAccountGetCommodity(s->acc);
    1066                 :         403 :         if (com_scratch && gnc_commodity_is_currency(com_scratch))
    1067                 :             :         {
    1068                 :          71 :             curr_weight = 3;
    1069                 :             :         }
    1070                 :             :         else
    1071                 :             :         {
    1072                 :         332 :             com_scratch = gnc_account_get_currency_or_parent(s->acc);
    1073                 :         332 :             if (com_scratch == nullptr) continue;
    1074                 :         329 :             curr_weight = 1;
    1075                 :             :         }
    1076                 :         400 :         if ( comlist )
    1077                 :             :         {
    1078                 :         202 :             found = g_slist_find_custom(comlist, com_scratch, commodity_equal);
    1079                 :             :         }
    1080                 :         400 :         if (comlist == nullptr || found == nullptr)
    1081                 :             :         {
    1082                 :         198 :             CommodityCount *count = g_slice_new0(CommodityCount);
    1083                 :         198 :             count->commodity = com_scratch;
    1084                 :         198 :             count->count = curr_weight;
    1085                 :         198 :             comlist = g_slist_append(comlist, count);
    1086                 :         198 :         }
    1087                 :             :         else
    1088                 :             :         {
    1089                 :         202 :             CommodityCount *count = (CommodityCount*)(found->data);
    1090                 :         202 :             count->count += curr_weight;
    1091                 :             :         }
    1092                 :             :     }
    1093                 :         198 :     found = g_slist_sort( comlist, commodity_compare);
    1094                 :             : 
    1095                 :         198 :     if ( found && found->data && (((CommodityCount*)(found->data))->commodity != nullptr))
    1096                 :             :     {
    1097                 :         198 :         return ((CommodityCount*)(found->data))->commodity;
    1098                 :             :     }
    1099                 :             :     /* We didn't find a currency in the current account structure, so try
    1100                 :             :      * an old one. */
    1101                 :           0 :     return xaccTransFindOldCommonCurrency( trans, book );
    1102                 :             : }
    1103                 :             : 
    1104                 :             : /* ================================================================ */
    1105                 :             : 
    1106                 :             : void
    1107                 :        1553 : xaccTransScrubCurrency (Transaction *trans)
    1108                 :             : {
    1109                 :             :     SplitList *node;
    1110                 :             :     gnc_commodity *currency;
    1111                 :             : 
    1112                 :        1553 :     if (!trans) return;
    1113                 :             : 
    1114                 :             :     /* If there are any orphaned splits in a transaction, then the
    1115                 :             :      * this routine will fail.  Therefore, we want to make sure that
    1116                 :             :      * there are no orphans (splits without parent account).
    1117                 :             :      */
    1118                 :        1553 :     xaccTransScrubOrphans (trans);
    1119                 :             : 
    1120                 :        1553 :     currency = xaccTransGetCurrency (trans);
    1121                 :        1553 :     if (currency && gnc_commodity_is_currency(currency)) return;
    1122                 :             : 
    1123                 :         198 :     currency = xaccTransFindCommonCurrency (trans, qof_instance_get_book(trans));
    1124                 :         198 :     if (currency)
    1125                 :             :     {
    1126                 :         198 :         xaccTransBeginEdit (trans);
    1127                 :         198 :         xaccTransSetCurrency (trans, currency);
    1128                 :         198 :         xaccTransCommitEdit (trans);
    1129                 :             :     }
    1130                 :             :     else
    1131                 :             :     {
    1132                 :           0 :         if (nullptr == trans->splits)
    1133                 :             :         {
    1134                 :           0 :             PWARN ("Transaction \"%s\" has no splits in it!", trans->description);
    1135                 :             :         }
    1136                 :             :         else
    1137                 :             :         {
    1138                 :             :             SplitList *node;
    1139                 :             :             char guid_str[GUID_ENCODING_LENGTH + 1];
    1140                 :           0 :             guid_to_string_buff(xaccTransGetGUID(trans), guid_str);
    1141                 :           0 :             PWARN ("no common transaction currency found for trans=\"%s\" (%s);",
    1142                 :             :                    trans->description, guid_str);
    1143                 :             : 
    1144                 :           0 :             for (node = trans->splits; node; node = node->next)
    1145                 :             :             {
    1146                 :           0 :                 Split *split = GNC_SPLIT(node->data);
    1147                 :           0 :                 if (nullptr == split->acc)
    1148                 :             :                 {
    1149                 :           0 :                     PWARN (" split=\"%s\" is not in any account!", split->memo);
    1150                 :             :                 }
    1151                 :             :                 else
    1152                 :             :                 {
    1153                 :           0 :                     gnc_commodity *currency = xaccAccountGetCommodity(split->acc);
    1154                 :           0 :                     PWARN ("setting to split=\"%s\" account=\"%s\" commodity=\"%s\"",
    1155                 :             :                            split->memo, xaccAccountGetName(split->acc),
    1156                 :             :                            gnc_commodity_get_mnemonic(currency));
    1157                 :             : 
    1158                 :           0 :                     xaccTransBeginEdit (trans);
    1159                 :           0 :                     xaccTransSetCurrency (trans, currency);
    1160                 :           0 :                     xaccTransCommitEdit (trans);
    1161                 :           0 :                     return;
    1162                 :             :                 }
    1163                 :             :             }
    1164                 :             :         }
    1165                 :           0 :         return;
    1166                 :             :     }
    1167                 :             : 
    1168                 :         601 :     for (node = trans->splits; node; node = node->next)
    1169                 :             :     {
    1170                 :         403 :         Split *sp = GNC_SPLIT(node->data);
    1171                 :             : 
    1172                 :         403 :         if (!gnc_numeric_equal(xaccSplitGetAmount (sp),
    1173                 :             :                                xaccSplitGetValue (sp)))
    1174                 :             :         {
    1175                 :             :             gnc_commodity *acc_currency;
    1176                 :             : 
    1177                 :         299 :             acc_currency = sp->acc ? xaccAccountGetCommodity(sp->acc) : nullptr;
    1178                 :         299 :             if (acc_currency == currency)
    1179                 :             :             {
    1180                 :             :                 /* This Split needs fixing: The transaction-currency equals
    1181                 :             :                  * the account-currency/commodity, but the amount/values are
    1182                 :             :                  * inequal i.e. they still correspond to the security
    1183                 :             :                  * (amount) and the currency (value). In the new model, the
    1184                 :             :                  * value is the amount in the account-commodity -- so it
    1185                 :             :                  * needs to be set to equal the amount (since the
    1186                 :             :                  * account-currency doesn't exist anymore).
    1187                 :             :                  *
    1188                 :             :                  * Note: Nevertheless we lose some information here. Namely,
    1189                 :             :                  * the information that the 'amount' in 'account-old-security'
    1190                 :             :                  * was worth 'value' in 'account-old-currency'. Maybe it would
    1191                 :             :                  * be better to store that information in the price database?
    1192                 :             :                  * But then, for old currency transactions there is still the
    1193                 :             :                  * 'other' transaction, which is going to keep that
    1194                 :             :                  * information. So I don't bother with that here. -- cstim,
    1195                 :             :                  * 2002/11/20. */
    1196                 :             : 
    1197                 :           0 :                 PWARN ("Adjusted split with mismatched values, desc=\"%s\" memo=\"%s\""
    1198                 :             :                        " old amount %s %s, new amount %s",
    1199                 :             :                        trans->description, sp->memo,
    1200                 :             :                        gnc_num_dbg_to_string (xaccSplitGetAmount(sp)),
    1201                 :             :                        gnc_commodity_get_mnemonic (currency),
    1202                 :             :                        gnc_num_dbg_to_string (xaccSplitGetValue(sp)));
    1203                 :           0 :                 xaccTransBeginEdit (trans);
    1204                 :           0 :                 xaccSplitSetAmount (sp, xaccSplitGetValue(sp));
    1205                 :           0 :                 xaccTransCommitEdit (trans);
    1206                 :             :             }
    1207                 :             :             /*else
    1208                 :             :               {
    1209                 :             :               PINFO ("Ok: Split '%s' Amount %s %s, value %s %s",
    1210                 :             :               xaccSplitGetMemo (sp),
    1211                 :             :               gnc_num_dbg_to_string (amount),
    1212                 :             :               gnc_commodity_get_mnemonic (currency),
    1213                 :             :               gnc_num_dbg_to_string (value),
    1214                 :             :               gnc_commodity_get_mnemonic (acc_currency));
    1215                 :             :               }*/
    1216                 :             :         }
    1217                 :             :     }
    1218                 :             : 
    1219                 :             : }
    1220                 :             : 
    1221                 :             : /* ================================================================ */
    1222                 :             : 
    1223                 :             : void
    1224                 :        2543 : xaccAccountScrubCommodity (Account *account)
    1225                 :             : {
    1226                 :             :     gnc_commodity *commodity;
    1227                 :             : 
    1228                 :        2543 :     if (!account) return;
    1229                 :        2543 :     if (xaccAccountGetType(account) == ACCT_TYPE_ROOT) return;
    1230                 :             : 
    1231                 :        2466 :     commodity = xaccAccountGetCommodity (account);
    1232                 :        2466 :     if (commodity) return;
    1233                 :             : 
    1234                 :             :     /* Use the 'obsolete' routines to try to figure out what the
    1235                 :             :      * account commodity should have been. */
    1236                 :         908 :     commodity = xaccAccountGetCommodity (account);
    1237                 :         908 :     if (commodity)
    1238                 :             :     {
    1239                 :           0 :         xaccAccountSetCommodity (account, commodity);
    1240                 :           0 :         return;
    1241                 :             :     }
    1242                 :             : 
    1243                 :         908 :     commodity = DxaccAccountGetCurrency (account);
    1244                 :         908 :     if (commodity)
    1245                 :             :     {
    1246                 :           0 :         xaccAccountSetCommodity (account, commodity);
    1247                 :           0 :         return;
    1248                 :             :     }
    1249                 :             : 
    1250                 :         908 :     PERR ("Account \"%s\" does not have a commodity!",
    1251                 :             :           xaccAccountGetName(account));
    1252                 :             : }
    1253                 :             : 
    1254                 :             : /* ================================================================ */
    1255                 :             : 
    1256                 :             : /* EFFECTIVE FRIEND FUNCTION declared in qofinstance-p.h */
    1257                 :             : extern void qof_instance_set_dirty (QofInstance*);
    1258                 :             : 
    1259                 :             : static void
    1260                 :         456 : xaccAccountDeleteOldData (Account *account)
    1261                 :             : {
    1262                 :         456 :     if (!account) return;
    1263                 :         456 :     xaccAccountBeginEdit (account);
    1264                 :         456 :     qof_instance_set_kvp (QOF_INSTANCE (account), nullptr, 1, "old-currency");
    1265                 :         456 :     qof_instance_set_kvp (QOF_INSTANCE (account), nullptr, 1, "old-security");
    1266                 :         456 :     qof_instance_set_kvp (QOF_INSTANCE (account), nullptr, 1, "old-currency-scu");
    1267                 :         456 :     qof_instance_set_kvp (QOF_INSTANCE (account), nullptr, 1, "old-security-scu");
    1268                 :         456 :     qof_instance_set_dirty (QOF_INSTANCE (account));
    1269                 :         456 :     xaccAccountCommitEdit (account);
    1270                 :             : }
    1271                 :             : 
    1272                 :             : static int
    1273                 :         774 : scrub_trans_currency_helper (Transaction *t, gpointer data)
    1274                 :             : {
    1275                 :         774 :     xaccTransScrubCurrency (t);
    1276                 :         774 :     return 0;
    1277                 :             : }
    1278                 :             : 
    1279                 :             : static void
    1280                 :         456 : scrub_account_commodity_helper (Account *account, gpointer data)
    1281                 :             : {
    1282                 :         456 :     scrub_depth++;
    1283                 :         456 :     xaccAccountScrubCommodity (account);
    1284                 :         456 :     xaccAccountDeleteOldData (account);
    1285                 :         456 :     scrub_depth--;
    1286                 :         456 : }
    1287                 :             : 
    1288                 :             : void
    1289                 :          22 : xaccAccountTreeScrubCommodities (Account *acc)
    1290                 :             : {
    1291                 :          22 :     if (!acc) return;
    1292                 :          22 :     scrub_depth++;
    1293                 :          22 :     xaccAccountTreeForEachTransaction (acc, scrub_trans_currency_helper, nullptr);
    1294                 :             : 
    1295                 :          22 :     scrub_account_commodity_helper (acc, nullptr);
    1296                 :          22 :     gnc_account_foreach_descendant (acc, scrub_account_commodity_helper, nullptr);
    1297                 :          22 :     scrub_depth--;
    1298                 :             : }
    1299                 :             : 
    1300                 :             : /* ================================================================ */
    1301                 :             : 
    1302                 :             : static gboolean
    1303                 :        5080 : check_quote_source (gnc_commodity *com, gpointer data)
    1304                 :             : {
    1305                 :        5080 :     gboolean *commodity_has_quote_src = (gboolean *)data;
    1306                 :        5080 :     if (com && !gnc_commodity_is_iso(com))
    1307                 :          86 :         *commodity_has_quote_src |= gnc_commodity_get_quote_flag(com);
    1308                 :        5080 :     return TRUE;
    1309                 :             : }
    1310                 :             : 
    1311                 :             : static void
    1312                 :         456 : move_quote_source (Account *account, gpointer data)
    1313                 :             : {
    1314                 :             :     gnc_commodity *com;
    1315                 :             :     gnc_quote_source *quote_source;
    1316                 :         456 :     gboolean new_style = GPOINTER_TO_INT(data);
    1317                 :             :     const char *source, *tz;
    1318                 :             : 
    1319                 :         456 :     com = xaccAccountGetCommodity(account);
    1320                 :         456 :     if (!com)
    1321                 :          19 :         return;
    1322                 :             : 
    1323                 :         437 :     if (!new_style)
    1324                 :             :     {
    1325                 :         424 :         source = dxaccAccountGetPriceSrc(account);
    1326                 :         424 :         if (!source || !*source)
    1327                 :         424 :             return;
    1328                 :           0 :         tz = dxaccAccountGetQuoteTZ(account);
    1329                 :             : 
    1330                 :           0 :         PINFO("to %8s from %s", gnc_commodity_get_mnemonic(com),
    1331                 :             :               xaccAccountGetName(account));
    1332                 :           0 :         gnc_commodity_set_quote_flag(com, TRUE);
    1333                 :           0 :         quote_source = gnc_quote_source_lookup_by_internal(source);
    1334                 :           0 :         if (!quote_source)
    1335                 :           0 :             quote_source = gnc_quote_source_add_new(source, FALSE);
    1336                 :           0 :         gnc_commodity_set_quote_source(com, quote_source);
    1337                 :           0 :         gnc_commodity_set_quote_tz(com, tz);
    1338                 :             :     }
    1339                 :             : 
    1340                 :          13 :     dxaccAccountSetPriceSrc(account, nullptr);
    1341                 :          13 :     dxaccAccountSetQuoteTZ(account, nullptr);
    1342                 :          13 :     return;
    1343                 :             : }
    1344                 :             : 
    1345                 :             : 
    1346                 :             : void
    1347                 :          22 : xaccAccountTreeScrubQuoteSources (Account *root, gnc_commodity_table *table)
    1348                 :             : {
    1349                 :          22 :     gboolean new_style = FALSE;
    1350                 :          22 :     ENTER(" ");
    1351                 :             : 
    1352                 :          22 :     if (!root || !table)
    1353                 :             :     {
    1354                 :           0 :         LEAVE("Oops");
    1355                 :           0 :         return;
    1356                 :             :     }
    1357                 :          22 :     scrub_depth++;
    1358                 :          22 :     gnc_commodity_table_foreach_commodity (table, check_quote_source, &new_style);
    1359                 :             : 
    1360                 :          22 :     move_quote_source(root, GINT_TO_POINTER(new_style));
    1361                 :          22 :     gnc_account_foreach_descendant (root, move_quote_source,
    1362                 :          22 :                                     GINT_TO_POINTER(new_style));
    1363                 :          22 :     LEAVE("Migration done");
    1364                 :          22 :     scrub_depth--;
    1365                 :             : }
    1366                 :             : 
    1367                 :             : /* ================================================================ */
    1368                 :             : 
    1369                 :             : void
    1370                 :         455 : xaccAccountScrubKvp (Account *account)
    1371                 :             : {
    1372                 :         455 :     GValue v = G_VALUE_INIT;
    1373                 :             :     gchar *str2;
    1374                 :             : 
    1375                 :         455 :     if (!account) return;
    1376                 :         455 :     scrub_depth++;
    1377                 :             : 
    1378                 :         455 :     qof_instance_get_kvp (QOF_INSTANCE (account), &v, 1, "notes");
    1379                 :         455 :     if (G_VALUE_HOLDS_STRING (&v))
    1380                 :             :     {
    1381                 :           1 :         str2 = g_strstrip(g_value_dup_string(&v));
    1382                 :           1 :         if (strlen(str2) == 0)
    1383                 :           0 :             qof_instance_slot_delete (QOF_INSTANCE (account), "notes");
    1384                 :           1 :         g_free(str2);
    1385                 :             :     }
    1386                 :             : 
    1387                 :         455 :     qof_instance_get_kvp (QOF_INSTANCE (account), &v, 1, "placeholder");
    1388                 :         455 :     if ((G_VALUE_HOLDS_STRING (&v) &&
    1389                 :         910 :         strcmp(g_value_get_string (&v), "false") == 0) ||
    1390                 :         455 :         (G_VALUE_HOLDS_BOOLEAN (&v) && ! g_value_get_boolean (&v)))
    1391                 :           0 :         qof_instance_slot_delete (QOF_INSTANCE (account), "placeholder");
    1392                 :             : 
    1393                 :         455 :     g_value_unset (&v);
    1394                 :         455 :     qof_instance_slot_delete_if_empty (QOF_INSTANCE (account), "hbci");
    1395                 :         455 :     scrub_depth--;
    1396                 :             : }
    1397                 :             : 
    1398                 :             : /* ================================================================ */
    1399                 :             : 
    1400                 :             : void
    1401                 :           0 : xaccAccountScrubColorNotSet (QofBook *book)
    1402                 :             : {
    1403                 :           0 :     GValue value_s = G_VALUE_INIT;
    1404                 :             :     gboolean already_scrubbed;
    1405                 :             : 
    1406                 :             :     // get the run-once value
    1407                 :           0 :     qof_instance_get_kvp (QOF_INSTANCE (book), &value_s, 1, "remove-color-not-set-slots");
    1408                 :             : 
    1409                 :           0 :     already_scrubbed = (G_VALUE_HOLDS_STRING (&value_s) &&
    1410                 :           0 :                         !g_strcmp0 (g_value_get_string (&value_s), "true"));
    1411                 :           0 :     g_value_unset (&value_s);
    1412                 :             : 
    1413                 :           0 :     if (already_scrubbed)
    1414                 :           0 :         return;
    1415                 :             :     else
    1416                 :             :     {
    1417                 :           0 :         GValue value_b = G_VALUE_INIT;
    1418                 :           0 :         Account *root = gnc_book_get_root_account (book);
    1419                 :           0 :         GList *accts = gnc_account_get_descendants_sorted (root);
    1420                 :             :         GList *ptr;
    1421                 :             : 
    1422                 :           0 :         for (ptr = accts; ptr; ptr = g_list_next (ptr))
    1423                 :             :         {
    1424                 :           0 :             auto acct = GNC_ACCOUNT(ptr->data);
    1425                 :           0 :             auto color = xaccAccountGetColor (acct);
    1426                 :             : 
    1427                 :           0 :             if (g_strcmp0 (color, "Not Set") == 0)
    1428                 :           0 :                 xaccAccountSetColor (acct, "");
    1429                 :             :         }
    1430                 :           0 :         g_list_free (accts);
    1431                 :             : 
    1432                 :           0 :         g_value_init (&value_b, G_TYPE_BOOLEAN);
    1433                 :           0 :         g_value_set_boolean (&value_b, TRUE);
    1434                 :             : 
    1435                 :             :         // set the run-once value
    1436                 :           0 :         qof_instance_set_kvp (QOF_INSTANCE (book),  &value_b, 1, "remove-color-not-set-slots");
    1437                 :           0 :         g_value_unset (&value_b);
    1438                 :             :     }
    1439                 :             : }
    1440                 :             : 
    1441                 :             : /* ================================================================ */
    1442                 :             : 
    1443                 :             : static Account*
    1444                 :          79 : construct_account (Account *root, gnc_commodity *currency, const char *accname,
    1445                 :             :                    GNCAccountType acctype, gboolean placeholder)
    1446                 :             : {
    1447                 :          79 :     gnc_commodity* root_currency = find_root_currency ();
    1448                 :          79 :     Account *acc = xaccMallocAccount(gnc_account_get_book (root));
    1449                 :          79 :     xaccAccountBeginEdit (acc);
    1450                 :          79 :     if (accname && *accname)
    1451                 :          79 :          xaccAccountSetName (acc, accname);
    1452                 :          79 :     if (currency || root_currency)
    1453                 :          79 :          xaccAccountSetCommodity (acc, currency ? currency : root_currency);
    1454                 :          79 :     xaccAccountSetType (acc, acctype);
    1455                 :          79 :     xaccAccountSetPlaceholder (acc, placeholder);
    1456                 :             : 
    1457                 :             :     /* Hang the account off the root. */
    1458                 :          79 :     gnc_account_append_child (root, acc);
    1459                 :          79 :     xaccAccountCommitEdit (acc);
    1460                 :          79 :     return acc;
    1461                 :             : }
    1462                 :             : 
    1463                 :             : static Account*
    1464                 :           0 : find_root_currency_account_in_list (GList *acc_list)
    1465                 :             : {
    1466                 :           0 :     gnc_commodity* root_currency = find_root_currency();
    1467                 :           0 :     for (GList *node = acc_list; node; node = g_list_next (node))
    1468                 :             :     {
    1469                 :           0 :         Account *acc = GNC_ACCOUNT (node->data);
    1470                 :           0 :         gnc_commodity *acc_commodity = nullptr;
    1471                 :           0 :         if (G_UNLIKELY (!acc)) continue;
    1472                 :           0 :         acc_commodity = xaccAccountGetCommodity(acc);
    1473                 :           0 :         if (gnc_commodity_equiv (acc_commodity, root_currency))
    1474                 :           0 :             return acc;
    1475                 :             :     }
    1476                 :             : 
    1477                 :           0 :     return nullptr;
    1478                 :             : }
    1479                 :             : 
    1480                 :             : static Account*
    1481                 :           0 : find_account_matching_name_in_list (GList *acc_list, const char* accname)
    1482                 :             : {
    1483                 :           0 :     for (GList* node = acc_list; node; node = g_list_next(node))
    1484                 :             :     {
    1485                 :           0 :         Account *acc = GNC_ACCOUNT (node->data);
    1486                 :           0 :         if (G_UNLIKELY (!acc)) continue;
    1487                 :           0 :         if (g_strcmp0 (accname, xaccAccountGetName (acc)) == 0)
    1488                 :           0 :             return acc;
    1489                 :             :     }
    1490                 :           0 :     return nullptr;
    1491                 :             : }
    1492                 :             : 
    1493                 :             : Account *
    1494                 :          95 : xaccScrubUtilityGetOrMakeAccount (Account *root, gnc_commodity * currency,
    1495                 :             :                                   const char *accname, GNCAccountType acctype,
    1496                 :             :                                   gboolean placeholder, gboolean checkname)
    1497                 :             : {
    1498                 :             :     GList* acc_list;
    1499                 :          95 :     Account *acc = nullptr;
    1500                 :             : 
    1501                 :          95 :     g_return_val_if_fail (root, nullptr);
    1502                 :             : 
    1503                 :             :     acc_list =
    1504                 :          95 :         gnc_account_lookup_by_type_and_commodity (root,
    1505                 :             :                                                   checkname ? accname : nullptr,
    1506                 :             :                                                   acctype, currency);
    1507                 :             : 
    1508                 :          95 :     if (!acc_list)
    1509                 :          79 :         return construct_account (root, currency, accname,
    1510                 :          79 :                                   acctype, placeholder);
    1511                 :             : 
    1512                 :          16 :     if (g_list_next(acc_list))
    1513                 :             :     {
    1514                 :           0 :         if (!currency)
    1515                 :           0 :             acc = find_root_currency_account_in_list (acc_list);
    1516                 :             : 
    1517                 :           0 :         if (!acc)
    1518                 :           0 :             acc = find_account_matching_name_in_list (acc_list, accname);
    1519                 :             :     }
    1520                 :             : 
    1521                 :          16 :     if (!acc)
    1522                 :          16 :         acc = GNC_ACCOUNT (acc_list->data);
    1523                 :             : 
    1524                 :          16 :     g_list_free (acc_list);
    1525                 :          16 :     return acc;
    1526                 :             : }
    1527                 :             : 
    1528                 :             : void
    1529                 :         788 : xaccTransScrubPostedDate (Transaction *trans)
    1530                 :             : {
    1531                 :         788 :     time64 orig = xaccTransGetDate(trans);
    1532                 :         788 :     if(orig == INT64_MAX)
    1533                 :             :     {
    1534                 :           0 :         GDate date = xaccTransGetDatePostedGDate(trans);
    1535                 :           0 :         time64 time = gdate_to_time64(date);
    1536                 :           0 :         if(time != INT64_MAX)
    1537                 :             :         {
    1538                 :             :             // xaccTransSetDatePostedSecs handles committing the change.
    1539                 :           0 :             xaccTransSetDatePostedSecs(trans, time);
    1540                 :             :         }
    1541                 :             :     }
    1542                 :         788 : }
    1543                 :             : 
    1544                 :             : /* ==================== END OF FILE ==================== */
        

Generated by: LCOV version 2.0-1