LCOV - code coverage report
Current view: top level - libgnucash/engine - Scrub.cpp (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 47.7 % 682 325
Test Date: 2026-11-02 18:19:43 Functions: 51.9 % 52 27
Legend: Lines:     hit not hit

            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)); }); };
     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         1626 : TransScrubOrphansFast (Transaction *trans, Account *root)
     112              : {
     113         1626 :     g_return_if_fail (trans && trans->common_currency && root);
     114              : 
     115         4910 :     for (GList *node = trans->splits; node; node = node->next)
     116              :     {
     117         3300 :         Split *split = GNC_SPLIT(node->data);
     118         3300 :         if (abort_now) break;
     119              : 
     120         3300 :         if (split->acc) continue;
     121              : 
     122           61 :         DEBUG ("Found an orphan\n");
     123              : 
     124              :         gchar *accname = g_strconcat
     125           61 :             (_("Orphan"), "-", gnc_commodity_get_mnemonic (trans->common_currency),
     126              :              nullptr);
     127              : 
     128              :         Account *orph = xaccScrubUtilityGetOrMakeAccount
     129           61 :             (root, trans->common_currency, accname, ACCT_TYPE_BANK, false, true);
     130              : 
     131           61 :         g_free (accname);
     132           61 :         if (!orph) continue;
     133              : 
     134           61 :         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         1626 : xaccTransScrubOrphans (Transaction *trans)
     180              : {
     181              :     SplitList *node;
     182         1626 :     QofBook *book = nullptr;
     183         1626 :     Account *root = nullptr;
     184              : 
     185         1626 :     if (!trans) return;
     186              : 
     187         1698 :     for (node = trans->splits; node; node = node->next)
     188              :     {
     189         1632 :         Split *split = GNC_SPLIT(node->data);
     190         1632 :         if (abort_now) break;
     191              : 
     192         1632 :         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           66 :     PINFO ("Free Floating Transaction!");
     205           66 :     book = xaccTransGetBook (trans);
     206           66 :     root = gnc_book_get_root_account (book);
     207           66 :     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         2522 :     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         8471 : 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         8471 :     if (!split) return false;
     246         8471 :     ENTER ("(split=%p)", split);
     247              : 
     248         8471 :     trans = xaccSplitGetParent (split);
     249         8471 :     if (!trans)
     250              :     {
     251            8 :         LEAVE("no trans");
     252            8 :         return false;
     253              :     }
     254              : 
     255         8463 :     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         8463 :     if (!account)
     261              :     {
     262          146 :         if (dry_run)
     263           73 :             return true;
     264              :         else
     265           73 :             xaccTransScrubOrphans (trans);
     266           73 :         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         8390 :     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         8374 :     value = xaccSplitGetValue (split);
     282         8374 :     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         8374 :     amount = xaccSplitGetAmount (split);
     292         8374 :     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         8374 :     currency = xaccTransGetCurrency (trans);
     302              : 
     303              :     /* If the account doesn't have a commodity,
     304              :      * we should attempt to fix that first.
     305              :      */
     306         8374 :     acc_commodity = xaccAccountGetCommodity(account);
     307         8374 :     if (!acc_commodity)
     308              :     {
     309         1362 :         if (dry_run)
     310          454 :             return true;
     311              :         else
     312          908 :             xaccAccountScrubCommodity (account);
     313              :     }
     314         7920 :     if (!acc_commodity || !gnc_commodity_equiv(acc_commodity, currency))
     315              :     {
     316         2796 :         LEAVE ("(split=%p) inequiv currency", split);
     317         2796 :         return false;
     318              :     }
     319              : 
     320         5124 :     scu = MIN (xaccAccountGetCommoditySCU (account),
     321              :                gnc_commodity_get_fraction (currency));
     322              : 
     323         5124 :     if (gnc_numeric_same (amount, value, scu, GNC_HOW_RND_ROUND_HALF_UP))
     324              :     {
     325         5057 :         LEAVE("(split=%p) different values", split);
     326         5057 :         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         3060 : xaccTransScrubSplits (Transaction *trans)
     396              : {
     397         3060 :     if (!trans) return;
     398              : 
     399         3060 :     gnc_commodity *currency = xaccTransGetCurrency (trans);
     400         3060 :     if (!currency)
     401          166 :         PERR ("Transaction doesn't have a currency!");
     402              : 
     403         3060 :     bool must_scrub = false;
     404              : 
     405        14504 :     for (GList *n = xaccTransGetSplitList (trans); !must_scrub && n; n = g_list_next (n))
     406         5722 :         if (split_scrub_or_dry_run (GNC_SPLIT(n->data), true))
     407          557 :             must_scrub = true;
     408              : 
     409         3060 :     if (!must_scrub)
     410         2503 :         return;
     411              : 
     412          557 :     xaccTransBeginEdit(trans);
     413              :     /* The split scrub expects the transaction to have a currency! */
     414              : 
     415         2737 :     for (GList *n = xaccTransGetSplitList (trans); n; n = g_list_next (n))
     416         1090 :         xaccSplitScrub (GNC_SPLIT(n->data));
     417              : 
     418          557 :     xaccTransCommitEdit(trans);
     419              : }
     420              : 
     421              : /* ================================================================ */
     422              : 
     423              : void
     424         2749 : xaccSplitScrub (Split *split)
     425              : {
     426         2749 :     split_scrub_or_dry_run (split, false);
     427         2749 : }
     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           46 : get_balance_split (Transaction *trans, Account *root, Account *account,
     446              :                    gnc_commodity *commodity)
     447              : {
     448              :     Split *balance_split;
     449              :     gchar *accname;
     450              : 
     451           46 :     if (!account ||
     452            0 :         !gnc_commodity_equiv (commodity, xaccAccountGetCommodity(account)))
     453              :     {
     454           46 :         if (!root)
     455              :         {
     456           46 :             root = gnc_book_get_root_account (xaccTransGetBook (trans));
     457           46 :             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           46 :         accname = g_strconcat (_("Imbalance"), "-",
     465              :                                gnc_commodity_get_mnemonic (commodity), nullptr);
     466           46 :         account = xaccScrubUtilityGetOrMakeAccount (root, commodity,
     467              :                                                     accname, ACCT_TYPE_BANK,
     468              :                                                     FALSE, TRUE);
     469           46 :         g_free (accname);
     470           46 :         if (!account)
     471              :         {
     472            0 :             PERR ("Can't get balancing account");
     473            0 :             return nullptr;
     474              :         }
     475              :     }
     476              : 
     477           46 :     balance_split = xaccTransFindSplitByAccount(trans, account);
     478              : 
     479              :     /* Put split into account before setting split value */
     480           46 :     if (!balance_split)
     481              :     {
     482           37 :         balance_split = xaccMallocSplit (qof_instance_get_book(trans));
     483              : 
     484           37 :         xaccTransBeginEdit (trans);
     485           37 :         xaccSplitSetParent(balance_split, trans);
     486           37 :         xaccSplitSetAccount(balance_split, account);
     487           37 :         xaccTransCommitEdit (trans);
     488              :     }
     489              : 
     490           46 :     return balance_split;
     491              : }
     492              : 
     493              : static gnc_commodity*
     494           83 : find_root_currency(void)
     495              : {
     496           83 :     QofSession *sess = gnc_get_current_session ();
     497           83 :     Account *root = gnc_book_get_root_account (qof_session_get_book (sess));
     498           83 :     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           83 :     if (!root_currency)
     504              :     {
     505           83 :          GList *children = gnc_account_get_children (root);
     506           87 :          for (GList *node = children; node && !root_currency;
     507            2 :               node = g_list_next (node))
     508              :          {
     509            2 :               Account *child = GNC_ACCOUNT (node->data);
     510            2 :               if (xaccAccountGetType (child) == ACCT_TYPE_INCOME)
     511            0 :                    root_currency = xaccAccountGetCommodity (child);
     512              :          }
     513           83 :          g_list_free (children);
     514              :     }
     515           83 :     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           46 : 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           46 :     gnc_commodity *currency = xaccTransGetCurrency (trans);
     589              : 
     590           46 :     balance_split = get_balance_split(trans, root, account, currency);
     591           46 :     if (!balance_split)
     592              :     {
     593              :         /* Error already logged */
     594            0 :         LEAVE("");
     595            0 :         return;
     596              :     }
     597              : 
     598           46 :     old_value = xaccSplitGetValue (balance_split);
     599              : 
     600              :     /* Note: We have to round for the commodity's fraction, NOT any
     601              :      * already existing denominator (bug #104343), because either one
     602              :      * of the denominators might already be reduced.  */
     603           46 :     new_value = gnc_numeric_sub (old_value, imbalance,
     604           46 :                                  gnc_commodity_get_fraction(currency),
     605              :                                  GNC_HOW_RND_ROUND_HALF_UP);
     606              : 
     607           46 :     if (gnc_numeric_zero_p (new_value))
     608              :     {
     609              :         const char *p;
     610            5 :         p = xaccSplitGetMemo (balance_split);
     611            5 :         if (!p || !*p)
     612              :         {
     613            5 :             p = xaccSplitGetAction (balance_split);
     614            5 :             if (!p || !*p)
     615              :             {
     616            5 :                 xaccSplitDestroy (balance_split);
     617            5 :                 return;
     618              :             }
     619              :         }
     620              :     }
     621              : 
     622           41 :     xaccTransBeginEdit (trans);
     623           41 :     xaccSplitSetValue (balance_split, new_value);
     624              :         
     625           41 :     account = xaccSplitGetAccount(balance_split);
     626           41 :     commodity = xaccAccountGetCommodity (account);
     627           41 :     if (gnc_commodity_equiv (currency, commodity))
     628              :     {
     629           41 :         xaccSplitSetAmount (balance_split, new_value);
     630              :     }
     631              : 
     632           41 :     xaccSplitScrub (balance_split);
     633           41 :     xaccTransCommitEdit (trans);
     634              : }
     635              : 
     636              : /* Balance a transaction without trading accounts. */
     637              : static void
     638           46 : gnc_transaction_balance_no_trading (Transaction *trans, Account *root,
     639              :                                     Account *account)
     640              : {
     641           46 :     gnc_numeric imbalance  = xaccTransGetImbalanceValue (trans);
     642              : 
     643              :     /* Make the value sum to zero */
     644           46 :     if (! gnc_numeric_zero_p (imbalance))
     645              :     {
     646           46 :         PINFO ("Value unbalanced transaction");
     647              : 
     648           46 :         add_balance_split (trans, imbalance, root, account);
     649              :     }
     650              : 
     651           46 : }
     652              : 
     653              : static gnc_numeric
     654            0 : gnc_transaction_get_commodity_imbalance (Transaction *trans,
     655              :                                          gnc_commodity *commodity)
     656              : {
     657              :     /* Find the value imbalance in this commodity */
     658            0 :     gnc_numeric val_imbalance = gnc_numeric_zero();
     659            0 :     GList *splits = nullptr;
     660            0 :     for (splits = trans->splits; splits; splits = splits->next)
     661              :     {
     662            0 :         Split *split = GNC_SPLIT(splits->data);
     663              :         gnc_commodity *split_commodity =
     664            0 :             xaccAccountGetCommodity(xaccSplitGetAccount(split));
     665            0 :         if (xaccTransStillHasSplit (trans, split) &&
     666            0 :             gnc_commodity_equal (commodity, split_commodity))
     667            0 :             val_imbalance = gnc_numeric_add (val_imbalance,
     668              :                                              xaccSplitGetValue (split),
     669              :                                              GNC_DENOM_AUTO,
     670              :                                              GNC_HOW_DENOM_EXACT);
     671              :     }
     672            0 :     return val_imbalance;
     673              : }
     674              : 
     675              : /* GFunc wrapper for xaccSplitDestroy */
     676              : static void
     677            0 : destroy_split (void* ptr)
     678              : {
     679            0 :     Split *split = GNC_SPLIT (ptr);
     680            0 :     if (split)
     681            0 :         xaccSplitDestroy (split);
     682            0 : }
     683              : 
     684              : /* Balancing transactions with trading accounts works best when
     685              :  * starting with no trading splits.
     686              :  */
     687              : static void
     688            0 : xaccTransClearTradingSplits (Transaction *trans)
     689              : {
     690            0 :     GList *trading_splits = nullptr;
     691              : 
     692            0 :     for (GList* node = trans->splits; node; node = node->next)
     693              :     {
     694            0 :          Split* split = GNC_SPLIT(node->data);
     695            0 :          Account* acc = nullptr;
     696            0 :          if (!split)
     697            0 :               continue;
     698            0 :          acc = xaccSplitGetAccount(split);
     699            0 :          if (acc && xaccAccountGetType(acc) == ACCT_TYPE_TRADING)
     700            0 :             trading_splits = g_list_prepend (trading_splits, node->data);
     701              :     }
     702              : 
     703            0 :     if (!trading_splits)
     704            0 :         return;
     705              : 
     706            0 :     xaccTransBeginEdit (trans);
     707              :     /* destroy_splits doesn't actually free the splits but this gets
     708              :      * the list itself freed.
     709              :      */
     710            0 :     g_list_free_full (trading_splits, destroy_split);
     711            0 :     xaccTransCommitEdit (trans);
     712              : }
     713              : 
     714              : static void
     715            0 : gnc_transaction_balance_trading (Transaction *trans, Account *root)
     716              : {
     717              :     MonetaryList *imbal_list;
     718              :     MonetaryList *imbalance_commod;
     719            0 :     Split *balance_split = nullptr;
     720              : 
     721              :     /* If the transaction is balanced, nothing more to do */
     722            0 :     imbal_list = xaccTransGetImbalance (trans);
     723            0 :     if (!imbal_list)
     724              :     {
     725            0 :         LEAVE("transaction is balanced");
     726            0 :         return;
     727              :     }
     728              : 
     729            0 :     PINFO ("Currency unbalanced transaction");
     730              : 
     731            0 :     for (imbalance_commod = imbal_list; imbalance_commod;
     732            0 :          imbalance_commod = imbalance_commod->next)
     733              :     {
     734            0 :         auto imbal_mon = static_cast<gnc_monetary*>(imbalance_commod->data);
     735              :         gnc_commodity *commodity;
     736              :         gnc_numeric old_amount, new_amount;
     737            0 :         const gnc_commodity *txn_curr = xaccTransGetCurrency (trans);
     738              : 
     739            0 :         commodity = gnc_monetary_commodity (*imbal_mon);
     740              : 
     741            0 :         balance_split = get_trading_split(trans, root, commodity);
     742            0 :         if (!balance_split)
     743              :         {
     744              :             /* Error already logged */
     745            0 :             gnc_monetary_list_free(imbal_list);
     746            0 :             LEAVE("");
     747            0 :             return;
     748              :         }
     749              : 
     750            0 :         xaccTransBeginEdit (trans);
     751              : 
     752            0 :         old_amount = xaccSplitGetAmount (balance_split);
     753            0 :         new_amount = gnc_numeric_sub (old_amount, gnc_monetary_value(*imbal_mon),
     754            0 :                                       gnc_commodity_get_fraction(commodity),
     755              :                                       GNC_HOW_RND_ROUND_HALF_UP);
     756              : 
     757            0 :         xaccSplitSetAmount (balance_split, new_amount);
     758              : 
     759            0 :         if (gnc_commodity_equal (txn_curr, commodity))
     760              :         {
     761              :             /* Imbalance commodity is the transaction currency, value in the
     762              :                split must be the same as the amount */
     763            0 :             xaccSplitSetValue (balance_split, new_amount);
     764              :         }
     765              :         else
     766              :         {
     767            0 :             gnc_numeric val_imbalance = gnc_transaction_get_commodity_imbalance (trans,            commodity);
     768              : 
     769            0 :             gnc_numeric old_value = xaccSplitGetValue (balance_split);
     770            0 :             gnc_numeric new_value = gnc_numeric_sub (old_value, val_imbalance,
     771            0 :                                          gnc_commodity_get_fraction(txn_curr),
     772              :                                          GNC_HOW_RND_ROUND_HALF_UP);
     773              : 
     774            0 :             xaccSplitSetValue (balance_split, new_value);
     775              :         }
     776              : 
     777            0 :         xaccSplitScrub (balance_split);
     778            0 :         xaccTransCommitEdit (trans);
     779              :     }
     780              : 
     781            0 :     gnc_monetary_list_free(imbal_list);
     782              : }
     783              : 
     784              : /** Balance the transaction by adding more trading splits. This shouldn't
     785              :  * ordinarily be necessary.
     786              :  * @param trans the transaction to balance
     787              :  * @param root the root account
     788              :  */
     789              : static void
     790            0 : gnc_transaction_balance_trading_more_splits (Transaction *trans, Account *root)
     791              : {
     792              :     /* Copy the split list so we don't see the splits we're adding */
     793            0 :     GList *splits_dup = g_list_copy(trans->splits), *splits = nullptr;
     794            0 :     const gnc_commodity  *txn_curr = xaccTransGetCurrency (trans);
     795            0 :     for (splits = splits_dup; splits; splits = splits->next)
     796              :     {
     797            0 :         Split *split = GNC_SPLIT(splits->data);
     798            0 :         if (! xaccTransStillHasSplit(trans, split)) continue;
     799            0 :         if (!gnc_numeric_zero_p(xaccSplitGetValue(split)) &&
     800            0 :             gnc_numeric_zero_p(xaccSplitGetAmount(split)))
     801              :         {
     802              :             gnc_commodity *commodity;
     803              :             gnc_numeric old_value, new_value;
     804              :             Split *balance_split;
     805              : 
     806            0 :             commodity = xaccAccountGetCommodity(xaccSplitGetAccount(split));
     807            0 :             if (!commodity)
     808              :             {
     809            0 :                 PERR("Split has no commodity");
     810            0 :                 continue;
     811              :             }
     812            0 :             balance_split = get_trading_split(trans, root, commodity);
     813            0 :             if (!balance_split)
     814              :             {
     815              :                 /* Error already logged */
     816            0 :                 LEAVE("");
     817            0 :                 return;
     818              :             }
     819            0 :             xaccTransBeginEdit (trans);
     820              : 
     821            0 :             old_value = xaccSplitGetValue (balance_split);
     822            0 :             new_value = gnc_numeric_sub (old_value, xaccSplitGetValue(split),
     823            0 :                                          gnc_commodity_get_fraction(txn_curr),
     824              :                                          GNC_HOW_RND_ROUND_HALF_UP);
     825            0 :             xaccSplitSetValue (balance_split, new_value);
     826              : 
     827              :             /* Don't change the balance split's amount since the amount
     828              :                is zero in the split we're working on */
     829              : 
     830            0 :             xaccSplitScrub (balance_split);
     831            0 :             xaccTransCommitEdit (trans);
     832              :         }
     833              :     }
     834              : 
     835            0 :     g_list_free(splits_dup);
     836              : }
     837              : 
     838              : /** Correct transaction imbalances.
     839              :  * @param trans The Transaction
     840              :  * @param root The (hidden) root account, for the book default currency.
     841              :  * @param account The account whose currency in which to balance.
     842              :  */
     843              : 
     844              : void
     845         3060 : xaccTransScrubImbalance (Transaction *trans, Account *root,
     846              :                          Account *account)
     847              : {
     848              :     gnc_numeric imbalance;
     849              : 
     850         6120 :     if (!trans) return;
     851              : 
     852         3060 :     ENTER ("()");
     853              : 
     854              :     /* Must look for orphan splits even if there is no imbalance. */
     855         3060 :     xaccTransScrubSplits (trans);
     856              : 
     857              :     /* Return immediately if things are balanced. */
     858         3060 :     if (xaccTransIsBalanced (trans))
     859              :     {
     860         3014 :         LEAVE ("transaction is balanced");
     861         3014 :         return;
     862              :     }
     863              : 
     864           46 :     if (! xaccTransUseTradingAccounts (trans))
     865              :     {
     866           46 :         gnc_transaction_balance_no_trading (trans, root, account);
     867           46 :         LEAVE ("transaction balanced, no managed trading accounts");
     868           46 :         return;
     869              :     }
     870              : 
     871            0 :     xaccTransClearTradingSplits (trans);
     872            0 :     imbalance = xaccTransGetImbalanceValue (trans);
     873            0 :     if (! gnc_numeric_zero_p (imbalance))
     874              :     {
     875            0 :         PINFO ("Value unbalanced transaction");
     876              : 
     877            0 :         add_balance_split (trans, imbalance, root, account);
     878              :     }
     879              : 
     880            0 :     gnc_transaction_balance_trading (trans, root);
     881            0 :     if (gnc_numeric_zero_p(xaccTransGetImbalanceValue(trans)))
     882              :     {
     883            0 :         LEAVE ("()");
     884            0 :         return;
     885              :     }
     886              :     /* If the transaction is still not balanced, it's probably because there
     887              :        are splits with zero amount and non-zero value.  These are usually
     888              :        realized gain/loss splits.  Add a reversing split for each of them to
     889              :        balance the value. */
     890              : 
     891            0 :     gnc_transaction_balance_trading_more_splits (trans, root);
     892            0 :     if (!gnc_numeric_zero_p(xaccTransGetImbalanceValue(trans)))
     893            0 :         PERR("Balancing currencies unbalanced value");
     894              : 
     895              : }
     896              : 
     897              : /* ================================================================ */
     898              : /* The xaccTransFindCommonCurrency () method returns
     899              :  *    a gnc_commodity indicating a currency denomination that all
     900              :  *    of the splits in this transaction have in common, using the
     901              :  *    old/obsolete currency/security fields of the split accounts.
     902              :  */
     903              : 
     904              : static gnc_commodity *
     905            0 : FindCommonExclSCurrency (SplitList *splits,
     906              :                          gnc_commodity * ra, gnc_commodity * rb,
     907              :                          Split *excl_split)
     908              : {
     909              :     GList *node;
     910              : 
     911            0 :     if (!splits) return nullptr;
     912              : 
     913            0 :     for (node = splits; node; node = node->next)
     914              :     {
     915            0 :         Split *s = GNC_SPLIT(node->data);
     916              :         gnc_commodity * sa, * sb;
     917              : 
     918            0 :         if (s == excl_split) continue;
     919              : 
     920            0 :         g_return_val_if_fail (s->acc, nullptr);
     921              : 
     922            0 :         sa = DxaccAccountGetCurrency (s->acc);
     923            0 :         sb = xaccAccountGetCommodity (s->acc);
     924              : 
     925            0 :         if (ra && rb)
     926              :         {
     927            0 :             int aa = !gnc_commodity_equiv(ra, sa);
     928            0 :             int ab = !gnc_commodity_equiv(ra, sb);
     929            0 :             int ba = !gnc_commodity_equiv(rb, sa);
     930            0 :             int bb = !gnc_commodity_equiv(rb, sb);
     931              : 
     932            0 :             if ( (!aa) && bb) rb = nullptr;
     933            0 :             else if ( (!ab) && ba) rb = nullptr;
     934            0 :             else if ( (!ba) && ab) ra = nullptr;
     935            0 :             else if ( (!bb) && aa) ra = nullptr;
     936            0 :             else if ( aa && bb && ab && ba )
     937              :             {
     938            0 :                 ra = nullptr;
     939            0 :                 rb = nullptr;
     940              :             }
     941              : 
     942            0 :             if (!ra)
     943              :             {
     944            0 :                 ra = rb;
     945            0 :                 rb = nullptr;
     946              :             }
     947            0 :         }
     948            0 :         else if (ra && !rb)
     949              :         {
     950            0 :             int aa = !gnc_commodity_equiv(ra, sa);
     951            0 :             int ab = !gnc_commodity_equiv(ra, sb);
     952            0 :             if ( aa && ab ) ra = nullptr;
     953            0 :         }
     954            0 :         else if (!ra && rb)
     955              :         {
     956            0 :             int aa = !gnc_commodity_equiv(rb, sa);
     957            0 :             int ab = !gnc_commodity_equiv(rb, sb);
     958            0 :             ra = ( aa && ab ) ? nullptr : rb;
     959              :         }
     960              : 
     961            0 :         if ((!ra) && (!rb)) return nullptr;
     962              :     }
     963              : 
     964            0 :     return (ra);
     965              : }
     966              : 
     967              : /* This is the wrapper for those calls (i.e. the older ones) which
     968              :  * don't exclude one split from the splitlist when looking for a
     969              :  * common currency.
     970              :  */
     971              : static gnc_commodity *
     972            0 : FindCommonCurrency (GList *splits, gnc_commodity * ra, gnc_commodity * rb)
     973              : {
     974            0 :     return FindCommonExclSCurrency(splits, ra, rb, nullptr);
     975              : }
     976              : 
     977              : static gnc_commodity *
     978            0 : xaccTransFindOldCommonCurrency (Transaction *trans, QofBook *book)
     979              : {
     980              :     gnc_commodity *ra, *rb, *retval;
     981              :     Split *split;
     982              : 
     983            0 :     if (!trans) return nullptr;
     984              : 
     985            0 :     if (trans->splits == nullptr) return nullptr;
     986              : 
     987            0 :     g_return_val_if_fail (book, nullptr);
     988              : 
     989            0 :     split = GNC_SPLIT(trans->splits->data);
     990              : 
     991            0 :     if (!split || nullptr == split->acc) return nullptr;
     992              : 
     993            0 :     ra = DxaccAccountGetCurrency (split->acc);
     994            0 :     rb = xaccAccountGetCommodity (split->acc);
     995              : 
     996            0 :     retval = FindCommonCurrency (trans->splits, ra, rb);
     997              : 
     998            0 :     if (retval && !gnc_commodity_is_currency(retval))
     999            0 :         retval = nullptr;
    1000              : 
    1001            0 :     return retval;
    1002              : }
    1003              : 
    1004              : /* Test the currency of the splits and find the most common and return
    1005              :  * it, or nullptr if there is no currency more common than the
    1006              :  * others -- or none at all.
    1007              :  */
    1008              : typedef struct
    1009              : {
    1010              :     gnc_commodity *commodity;
    1011              :     unsigned int count;
    1012              : } CommodityCount;
    1013              : 
    1014              : static gint
    1015          202 : commodity_equal (gconstpointer a, gconstpointer b)
    1016              : {
    1017          202 :     CommodityCount *cc = (CommodityCount*)a;
    1018          202 :     gnc_commodity *com = (gnc_commodity*)b;
    1019          404 :     if ( cc == nullptr || cc->commodity == nullptr ||
    1020          202 :          !GNC_IS_COMMODITY( cc->commodity ) ) return -1;
    1021          202 :     if ( com == nullptr || !GNC_IS_COMMODITY( com ) ) return 1;
    1022          202 :     if ( gnc_commodity_equal(cc->commodity, com) )
    1023          202 :         return 0;
    1024            0 :     return 1;
    1025              : }
    1026              : 
    1027              : static gint
    1028            0 : commodity_compare( gconstpointer a, gconstpointer b)
    1029              : {
    1030            0 :     CommodityCount *ca = (CommodityCount*)a, *cb = (CommodityCount*)b;
    1031            0 :     if (ca == nullptr || ca->commodity == nullptr ||
    1032            0 :         !GNC_IS_COMMODITY( ca->commodity ) )
    1033              :     {
    1034            0 :         if (cb == nullptr || cb->commodity == nullptr ||
    1035            0 :             !GNC_IS_COMMODITY( cb->commodity ) )
    1036            0 :             return 0;
    1037            0 :         return -1;
    1038              :     }
    1039            0 :     if (cb == nullptr || cb->commodity == nullptr ||
    1040            0 :         !GNC_IS_COMMODITY( cb->commodity ) )
    1041            0 :         return 1;
    1042            0 :     if (ca->count == cb->count)
    1043            0 :         return 0;
    1044            0 :     return ca->count > cb->count ? 1 : -1;
    1045              : }
    1046              : 
    1047              : /* Find the commodities in the account of each of the splits of a
    1048              :  * transaction, and rank them by how many splits in which they
    1049              :  * occur. Commodities which are currencies count more than those which
    1050              :  * aren't, because for simple buy and sell transactions it makes
    1051              :  * slightly more sense for the transaction commodity to be the
    1052              :  * currency -- to the extent that it makes sense for a transaction to
    1053              :  * have a currency at all. jralls, 2010-11-02 */
    1054              : 
    1055              : static gnc_commodity *
    1056          198 : xaccTransFindCommonCurrency (Transaction *trans, QofBook *book)
    1057              : {
    1058              :     gnc_commodity *com_scratch;
    1059          198 :     GList *node = nullptr;
    1060          198 :     GSList *comlist = nullptr, *found = nullptr;
    1061              : 
    1062          198 :     if (!trans) return nullptr;
    1063              : 
    1064          198 :     if (trans->splits == nullptr) return nullptr;
    1065              : 
    1066          198 :     g_return_val_if_fail (book, nullptr);
    1067              : 
    1068              :     /* Find the most commonly used currency among the splits.  If a given split
    1069              :        is in a non-currency commodity, then look for an ancestor account in a
    1070              :        currency, but prefer currencies used directly in splits.  Ignore trading
    1071              :        account splits in this whole process, they don't add any value to this algorithm. */
    1072          601 :     for (node = trans->splits; node; node = node->next)
    1073              :     {
    1074          403 :         Split *s = GNC_SPLIT(node->data);
    1075              :         unsigned int curr_weight;
    1076              : 
    1077          403 :         if (s == nullptr || s->acc == nullptr) continue;
    1078          403 :         if (xaccAccountGetType(s->acc) == ACCT_TYPE_TRADING) continue;
    1079          403 :         com_scratch = xaccAccountGetCommodity(s->acc);
    1080          403 :         if (com_scratch && gnc_commodity_is_currency(com_scratch))
    1081              :         {
    1082           71 :             curr_weight = 3;
    1083              :         }
    1084              :         else
    1085              :         {
    1086          332 :             com_scratch = gnc_account_get_currency_or_parent(s->acc);
    1087          332 :             if (com_scratch == nullptr) continue;
    1088          329 :             curr_weight = 1;
    1089              :         }
    1090          400 :         if ( comlist )
    1091              :         {
    1092          202 :             found = g_slist_find_custom(comlist, com_scratch, commodity_equal);
    1093              :         }
    1094          400 :         if (comlist == nullptr || found == nullptr)
    1095              :         {
    1096          198 :             CommodityCount *count = g_slice_new0(CommodityCount);
    1097          198 :             count->commodity = com_scratch;
    1098          198 :             count->count = curr_weight;
    1099          198 :             comlist = g_slist_append(comlist, count);
    1100          198 :         }
    1101              :         else
    1102              :         {
    1103          202 :             CommodityCount *count = (CommodityCount*)(found->data);
    1104          202 :             count->count += curr_weight;
    1105              :         }
    1106              :     }
    1107          198 :     found = g_slist_sort( comlist, commodity_compare);
    1108              : 
    1109          198 :     if ( found && found->data && (((CommodityCount*)(found->data))->commodity != nullptr))
    1110              :     {
    1111          198 :         return ((CommodityCount*)(found->data))->commodity;
    1112              :     }
    1113              :     /* We didn't find a currency in the current account structure, so try
    1114              :      * an old one. */
    1115            0 :     return xaccTransFindOldCommonCurrency( trans, book );
    1116              : }
    1117              : 
    1118              : /* ================================================================ */
    1119              : 
    1120              : void
    1121         1553 : xaccTransScrubCurrency (Transaction *trans)
    1122              : {
    1123              :     SplitList *node;
    1124              :     gnc_commodity *currency;
    1125              : 
    1126         1553 :     if (!trans) return;
    1127              : 
    1128              :     /* If there are any orphaned splits in a transaction, then the
    1129              :      * this routine will fail.  Therefore, we want to make sure that
    1130              :      * there are no orphans (splits without parent account).
    1131              :      */
    1132         1553 :     xaccTransScrubOrphans (trans);
    1133              : 
    1134         1553 :     currency = xaccTransGetCurrency (trans);
    1135         1553 :     if (currency && gnc_commodity_is_currency(currency)) return;
    1136              : 
    1137          198 :     currency = xaccTransFindCommonCurrency (trans, qof_instance_get_book(trans));
    1138          198 :     if (currency)
    1139              :     {
    1140          198 :         xaccTransBeginEdit (trans);
    1141          198 :         xaccTransSetCurrency (trans, currency);
    1142          198 :         xaccTransCommitEdit (trans);
    1143              :     }
    1144              :     else
    1145              :     {
    1146            0 :         if (nullptr == trans->splits)
    1147              :         {
    1148            0 :             PWARN ("Transaction \"%s\" has no splits in it!", trans->description);
    1149              :         }
    1150              :         else
    1151              :         {
    1152              :             SplitList *node;
    1153              :             char guid_str[GUID_ENCODING_LENGTH + 1];
    1154            0 :             guid_to_string_buff(xaccTransGetGUID(trans), guid_str);
    1155            0 :             PWARN ("no common transaction currency found for trans=\"%s\" (%s);",
    1156              :                    trans->description, guid_str);
    1157              : 
    1158            0 :             for (node = trans->splits; node; node = node->next)
    1159              :             {
    1160            0 :                 Split *split = GNC_SPLIT(node->data);
    1161            0 :                 if (nullptr == split->acc)
    1162              :                 {
    1163            0 :                     PWARN (" split=\"%s\" is not in any account!", split->memo);
    1164              :                 }
    1165              :                 else
    1166              :                 {
    1167            0 :                     gnc_commodity *currency = xaccAccountGetCommodity(split->acc);
    1168            0 :                     PWARN ("setting to split=\"%s\" account=\"%s\" commodity=\"%s\"",
    1169              :                            split->memo, xaccAccountGetName(split->acc),
    1170              :                            gnc_commodity_get_mnemonic(currency));
    1171              : 
    1172            0 :                     xaccTransBeginEdit (trans);
    1173            0 :                     xaccTransSetCurrency (trans, currency);
    1174            0 :                     xaccTransCommitEdit (trans);
    1175            0 :                     return;
    1176              :                 }
    1177              :             }
    1178              :         }
    1179            0 :         return;
    1180              :     }
    1181              : 
    1182          601 :     for (node = trans->splits; node; node = node->next)
    1183              :     {
    1184          403 :         Split *sp = GNC_SPLIT(node->data);
    1185              : 
    1186          403 :         if (!gnc_numeric_equal(xaccSplitGetAmount (sp),
    1187              :                                xaccSplitGetValue (sp)))
    1188              :         {
    1189              :             gnc_commodity *acc_currency;
    1190              : 
    1191          299 :             acc_currency = sp->acc ? xaccAccountGetCommodity(sp->acc) : nullptr;
    1192          299 :             if (acc_currency == currency)
    1193              :             {
    1194              :                 /* This Split needs fixing: The transaction-currency equals
    1195              :                  * the account-currency/commodity, but the amount/values are
    1196              :                  * inequal i.e. they still correspond to the security
    1197              :                  * (amount) and the currency (value). In the new model, the
    1198              :                  * value is the amount in the account-commodity -- so it
    1199              :                  * needs to be set to equal the amount (since the
    1200              :                  * account-currency doesn't exist anymore).
    1201              :                  *
    1202              :                  * Note: Nevertheless we lose some information here. Namely,
    1203              :                  * the information that the 'amount' in 'account-old-security'
    1204              :                  * was worth 'value' in 'account-old-currency'. Maybe it would
    1205              :                  * be better to store that information in the price database?
    1206              :                  * But then, for old currency transactions there is still the
    1207              :                  * 'other' transaction, which is going to keep that
    1208              :                  * information. So I don't bother with that here. -- cstim,
    1209              :                  * 2002/11/20. */
    1210              : 
    1211            0 :                 PWARN ("Adjusted split with mismatched values, desc=\"%s\" memo=\"%s\""
    1212              :                        " old amount %s %s, new amount %s",
    1213              :                        trans->description, sp->memo,
    1214              :                        gnc_num_dbg_to_string (xaccSplitGetAmount(sp)),
    1215              :                        gnc_commodity_get_mnemonic (currency),
    1216              :                        gnc_num_dbg_to_string (xaccSplitGetValue(sp)));
    1217            0 :                 xaccTransBeginEdit (trans);
    1218            0 :                 xaccSplitSetAmount (sp, xaccSplitGetValue(sp));
    1219            0 :                 xaccTransCommitEdit (trans);
    1220              :             }
    1221              :             /*else
    1222              :               {
    1223              :               PINFO ("Ok: Split '%s' Amount %s %s, value %s %s",
    1224              :               xaccSplitGetMemo (sp),
    1225              :               gnc_num_dbg_to_string (amount),
    1226              :               gnc_commodity_get_mnemonic (currency),
    1227              :               gnc_num_dbg_to_string (value),
    1228              :               gnc_commodity_get_mnemonic (acc_currency));
    1229              :               }*/
    1230              :         }
    1231              :     }
    1232              : 
    1233              : }
    1234              : 
    1235              : /* ================================================================ */
    1236              : 
    1237              : void
    1238         2543 : xaccAccountScrubCommodity (Account *account)
    1239              : {
    1240              :     gnc_commodity *commodity;
    1241              : 
    1242         2543 :     if (!account) return;
    1243         2543 :     if (xaccAccountGetType(account) == ACCT_TYPE_ROOT) return;
    1244              : 
    1245         2466 :     commodity = xaccAccountGetCommodity (account);
    1246         2466 :     if (commodity) return;
    1247              : 
    1248              :     /* Use the 'obsolete' routines to try to figure out what the
    1249              :      * account commodity should have been. */
    1250          908 :     commodity = xaccAccountGetCommodity (account);
    1251          908 :     if (commodity)
    1252              :     {
    1253            0 :         xaccAccountSetCommodity (account, commodity);
    1254            0 :         return;
    1255              :     }
    1256              : 
    1257          908 :     commodity = DxaccAccountGetCurrency (account);
    1258          908 :     if (commodity)
    1259              :     {
    1260            0 :         xaccAccountSetCommodity (account, commodity);
    1261            0 :         return;
    1262              :     }
    1263              : 
    1264          908 :     PERR ("Account \"%s\" does not have a commodity!",
    1265              :           xaccAccountGetName(account));
    1266              : }
    1267              : 
    1268              : /* ================================================================ */
    1269              : 
    1270              : /* EFFECTIVE FRIEND FUNCTION declared in qofinstance-p.h */
    1271              : extern void qof_instance_set_dirty (QofInstance*);
    1272              : 
    1273              : static void
    1274          456 : xaccAccountDeleteOldData (Account *account)
    1275              : {
    1276          456 :     if (!account) return;
    1277          456 :     xaccAccountBeginEdit (account);
    1278          456 :     qof_instance_set_kvp (QOF_INSTANCE (account), nullptr, 1, "old-currency");
    1279          456 :     qof_instance_set_kvp (QOF_INSTANCE (account), nullptr, 1, "old-security");
    1280          456 :     qof_instance_set_kvp (QOF_INSTANCE (account), nullptr, 1, "old-currency-scu");
    1281          456 :     qof_instance_set_kvp (QOF_INSTANCE (account), nullptr, 1, "old-security-scu");
    1282          456 :     qof_instance_set_dirty (QOF_INSTANCE (account));
    1283          456 :     xaccAccountCommitEdit (account);
    1284              : }
    1285              : 
    1286              : static int
    1287          774 : scrub_trans_currency_helper (Transaction *t, gpointer data)
    1288              : {
    1289          774 :     xaccTransScrubCurrency (t);
    1290          774 :     return 0;
    1291              : }
    1292              : 
    1293              : static void
    1294          456 : scrub_account_commodity_helper (Account *account, gpointer data)
    1295              : {
    1296          456 :     scrub_depth++;
    1297          456 :     xaccAccountScrubCommodity (account);
    1298          456 :     xaccAccountDeleteOldData (account);
    1299          456 :     scrub_depth--;
    1300          456 : }
    1301              : 
    1302              : void
    1303           22 : xaccAccountTreeScrubCommodities (Account *acc)
    1304              : {
    1305           22 :     if (!acc) return;
    1306           22 :     scrub_depth++;
    1307           22 :     xaccAccountTreeForEachTransaction (acc, scrub_trans_currency_helper, nullptr);
    1308              : 
    1309           22 :     scrub_account_commodity_helper (acc, nullptr);
    1310           22 :     gnc_account_foreach_descendant (acc, scrub_account_commodity_helper, nullptr);
    1311           22 :     scrub_depth--;
    1312              : }
    1313              : 
    1314              : /* ================================================================ */
    1315              : 
    1316              : static gboolean
    1317         5080 : check_quote_source (gnc_commodity *com, gpointer data)
    1318              : {
    1319         5080 :     gboolean *commodity_has_quote_src = (gboolean *)data;
    1320         5080 :     if (com && !gnc_commodity_is_iso(com))
    1321           86 :         *commodity_has_quote_src |= gnc_commodity_get_quote_flag(com);
    1322         5080 :     return TRUE;
    1323              : }
    1324              : 
    1325              : static void
    1326          456 : move_quote_source (Account *account, gpointer data)
    1327              : {
    1328              :     gnc_commodity *com;
    1329              :     gnc_quote_source *quote_source;
    1330          456 :     gboolean new_style = GPOINTER_TO_INT(data);
    1331              :     const char *source, *tz;
    1332              : 
    1333          456 :     com = xaccAccountGetCommodity(account);
    1334          456 :     if (!com)
    1335           19 :         return;
    1336              : 
    1337          437 :     if (!new_style)
    1338              :     {
    1339          424 :         source = dxaccAccountGetPriceSrc(account);
    1340          424 :         if (!source || !*source)
    1341          424 :             return;
    1342            0 :         tz = dxaccAccountGetQuoteTZ(account);
    1343              : 
    1344            0 :         PINFO("to %8s from %s", gnc_commodity_get_mnemonic(com),
    1345              :               xaccAccountGetName(account));
    1346            0 :         gnc_commodity_set_quote_flag(com, TRUE);
    1347            0 :         quote_source = gnc_quote_source_lookup_by_internal(source);
    1348            0 :         if (!quote_source)
    1349            0 :             quote_source = gnc_quote_source_add_new(source, FALSE);
    1350            0 :         gnc_commodity_set_quote_source(com, quote_source);
    1351            0 :         gnc_commodity_set_quote_tz(com, tz);
    1352              :     }
    1353              : 
    1354           13 :     dxaccAccountSetPriceSrc(account, nullptr);
    1355           13 :     dxaccAccountSetQuoteTZ(account, nullptr);
    1356           13 :     return;
    1357              : }
    1358              : 
    1359              : 
    1360              : void
    1361           22 : xaccAccountTreeScrubQuoteSources (Account *root, gnc_commodity_table *table)
    1362              : {
    1363           22 :     gboolean new_style = FALSE;
    1364           22 :     ENTER(" ");
    1365              : 
    1366           22 :     if (!root || !table)
    1367              :     {
    1368            0 :         LEAVE("Oops");
    1369            0 :         return;
    1370              :     }
    1371           22 :     scrub_depth++;
    1372           22 :     gnc_commodity_table_foreach_commodity (table, check_quote_source, &new_style);
    1373              : 
    1374           22 :     move_quote_source(root, GINT_TO_POINTER(new_style));
    1375           22 :     gnc_account_foreach_descendant (root, move_quote_source,
    1376           22 :                                     GINT_TO_POINTER(new_style));
    1377           22 :     LEAVE("Migration done");
    1378           22 :     scrub_depth--;
    1379              : }
    1380              : 
    1381              : /* ================================================================ */
    1382              : 
    1383              : void
    1384          455 : xaccAccountScrubKvp (Account *account)
    1385              : {
    1386          455 :     GValue v = G_VALUE_INIT;
    1387              :     gchar *str2;
    1388              : 
    1389          455 :     if (!account) return;
    1390          455 :     scrub_depth++;
    1391              : 
    1392          455 :     qof_instance_get_kvp (QOF_INSTANCE (account), &v, 1, "notes");
    1393          455 :     if (G_VALUE_HOLDS_STRING (&v))
    1394              :     {
    1395            1 :         str2 = g_strstrip(g_value_dup_string(&v));
    1396            1 :         if (strlen(str2) == 0)
    1397            0 :             qof_instance_slot_delete (QOF_INSTANCE (account), "notes");
    1398            1 :         g_free(str2);
    1399              :     }
    1400              : 
    1401          455 :     qof_instance_get_kvp (QOF_INSTANCE (account), &v, 1, "placeholder");
    1402          455 :     if ((G_VALUE_HOLDS_STRING (&v) &&
    1403          910 :         strcmp(g_value_get_string (&v), "false") == 0) ||
    1404          455 :         (G_VALUE_HOLDS_BOOLEAN (&v) && ! g_value_get_boolean (&v)))
    1405            0 :         qof_instance_slot_delete (QOF_INSTANCE (account), "placeholder");
    1406              : 
    1407          455 :     g_value_unset (&v);
    1408          455 :     qof_instance_slot_delete_if_empty (QOF_INSTANCE (account), "hbci");
    1409          455 :     scrub_depth--;
    1410              : }
    1411              : 
    1412              : /* ================================================================ */
    1413              : 
    1414              : void
    1415            0 : xaccAccountScrubColorNotSet (QofBook *book)
    1416              : {
    1417            0 :     GValue value_s = G_VALUE_INIT;
    1418              :     gboolean already_scrubbed;
    1419              : 
    1420              :     // get the run-once value
    1421            0 :     qof_instance_get_kvp (QOF_INSTANCE (book), &value_s, 1, "remove-color-not-set-slots");
    1422              : 
    1423            0 :     already_scrubbed = (G_VALUE_HOLDS_STRING (&value_s) &&
    1424            0 :                         !g_strcmp0 (g_value_get_string (&value_s), "true"));
    1425            0 :     g_value_unset (&value_s);
    1426              : 
    1427            0 :     if (already_scrubbed)
    1428            0 :         return;
    1429              :     else
    1430              :     {
    1431            0 :         GValue value_b = G_VALUE_INIT;
    1432            0 :         Account *root = gnc_book_get_root_account (book);
    1433            0 :         GList *accts = gnc_account_get_descendants_sorted (root);
    1434              :         GList *ptr;
    1435              : 
    1436            0 :         for (ptr = accts; ptr; ptr = g_list_next (ptr))
    1437              :         {
    1438            0 :             auto acct = GNC_ACCOUNT(ptr->data);
    1439            0 :             auto color = xaccAccountGetColor (acct);
    1440              : 
    1441            0 :             if (g_strcmp0 (color, "Not Set") == 0)
    1442            0 :                 xaccAccountSetColor (acct, "");
    1443              :         }
    1444            0 :         g_list_free (accts);
    1445              : 
    1446            0 :         g_value_init (&value_b, G_TYPE_BOOLEAN);
    1447            0 :         g_value_set_boolean (&value_b, TRUE);
    1448              : 
    1449              :         // set the run-once value
    1450            0 :         qof_instance_set_kvp (QOF_INSTANCE (book),  &value_b, 1, "remove-color-not-set-slots");
    1451            0 :         g_value_unset (&value_b);
    1452              :     }
    1453              : }
    1454              : 
    1455              : /* ================================================================ */
    1456              : 
    1457              : static Account*
    1458           83 : construct_account (Account *root, gnc_commodity *currency, const char *accname,
    1459              :                    GNCAccountType acctype, gboolean placeholder)
    1460              : {
    1461           83 :     gnc_commodity* root_currency = find_root_currency ();
    1462           83 :     Account *acc = xaccMallocAccount(gnc_account_get_book (root));
    1463           83 :     xaccAccountBeginEdit (acc);
    1464           83 :     if (accname && *accname)
    1465           83 :          xaccAccountSetName (acc, accname);
    1466           83 :     if (currency || root_currency)
    1467           83 :          xaccAccountSetCommodity (acc, currency ? currency : root_currency);
    1468           83 :     xaccAccountSetType (acc, acctype);
    1469           83 :     xaccAccountSetPlaceholder (acc, placeholder);
    1470              : 
    1471              :     /* Hang the account off the root. */
    1472           83 :     gnc_account_append_child (root, acc);
    1473           83 :     xaccAccountCommitEdit (acc);
    1474           83 :     return acc;
    1475              : }
    1476              : 
    1477              : static Account*
    1478            0 : find_root_currency_account_in_list (GList *acc_list)
    1479              : {
    1480            0 :     gnc_commodity* root_currency = find_root_currency();
    1481            0 :     for (GList *node = acc_list; node; node = g_list_next (node))
    1482              :     {
    1483            0 :         Account *acc = GNC_ACCOUNT (node->data);
    1484            0 :         gnc_commodity *acc_commodity = nullptr;
    1485            0 :         if (G_UNLIKELY (!acc)) continue;
    1486            0 :         acc_commodity = xaccAccountGetCommodity(acc);
    1487            0 :         if (gnc_commodity_equiv (acc_commodity, root_currency))
    1488            0 :             return acc;
    1489              :     }
    1490              : 
    1491            0 :     return nullptr;
    1492              : }
    1493              : 
    1494              : static Account*
    1495            0 : find_account_matching_name_in_list (GList *acc_list, const char* accname)
    1496              : {
    1497            0 :     for (GList* node = acc_list; node; node = g_list_next(node))
    1498              :     {
    1499            0 :         Account *acc = GNC_ACCOUNT (node->data);
    1500            0 :         if (G_UNLIKELY (!acc)) continue;
    1501            0 :         if (g_strcmp0 (accname, xaccAccountGetName (acc)) == 0)
    1502            0 :             return acc;
    1503              :     }
    1504            0 :     return nullptr;
    1505              : }
    1506              : 
    1507              : Account *
    1508          107 : xaccScrubUtilityGetOrMakeAccount (Account *root, gnc_commodity * currency,
    1509              :                                   const char *accname, GNCAccountType acctype,
    1510              :                                   gboolean placeholder, gboolean checkname)
    1511              : {
    1512              :     GList* acc_list;
    1513          107 :     Account *acc = nullptr;
    1514              : 
    1515          107 :     g_return_val_if_fail (root, nullptr);
    1516              : 
    1517              :     acc_list =
    1518          107 :         gnc_account_lookup_by_type_and_commodity (root,
    1519              :                                                   checkname ? accname : nullptr,
    1520              :                                                   acctype, currency);
    1521              : 
    1522          107 :     if (!acc_list)
    1523           83 :         return construct_account (root, currency, accname,
    1524           83 :                                   acctype, placeholder);
    1525              : 
    1526           24 :     if (g_list_next(acc_list))
    1527              :     {
    1528            0 :         if (!currency)
    1529            0 :             acc = find_root_currency_account_in_list (acc_list);
    1530              : 
    1531            0 :         if (!acc)
    1532            0 :             acc = find_account_matching_name_in_list (acc_list, accname);
    1533              :     }
    1534              : 
    1535           24 :     if (!acc)
    1536           24 :         acc = GNC_ACCOUNT (acc_list->data);
    1537              : 
    1538           24 :     g_list_free (acc_list);
    1539           24 :     return acc;
    1540              : }
    1541              : 
    1542              : void
    1543          791 : xaccTransScrubPostedDate (Transaction *trans)
    1544              : {
    1545          791 :     time64 orig = xaccTransGetDate(trans);
    1546          791 :     if(orig == INT64_MAX)
    1547              :     {
    1548            0 :         GDate date = xaccTransGetDatePostedGDate(trans);
    1549            0 :         time64 time = gdate_to_time64(date);
    1550            0 :         if(time != INT64_MAX)
    1551              :         {
    1552              :             // xaccTransSetDatePostedSecs handles committing the change.
    1553            0 :             xaccTransSetDatePostedSecs(trans, time);
    1554              :         }
    1555              :     }
    1556          791 : }
    1557              : 
    1558              : /* ==================== END OF FILE ==================== */
        

Generated by: LCOV version 2.0-1