LCOV - code coverage report
Current view: top level - libgnucash/engine - Split.cpp (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 84.1 % 983 827
Test Date: 2026-11-02 18:19:43 Functions: 82.8 % 99 82
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /********************************************************************\
       2              :  * Split.c -- split implementation                                  *
       3              :  * Copyright (C) 1997 Robin D. Clark                                *
       4              :  * Copyright (C) 1997-2003 Linas Vepstas <linas@linas.org>          *
       5              :  * Copyright (C) 2000 Bill Gribble <grib@billgribble.com>           *
       6              :  * Copyright (c) 2006 David Hampton <hampton@employees.org>         *
       7              :  *                                                                  *
       8              :  * This program is free software; you can redistribute it and/or    *
       9              :  * modify it under the terms of the GNU General Public License as   *
      10              :  * published by the Free Software Foundation; either version 2 of   *
      11              :  * the License, or (at your option) any later version.              *
      12              :  *                                                                  *
      13              :  * This program is distributed in the hope that it will be useful,  *
      14              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
      15              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
      16              :  * GNU General Public License for more details.                     *
      17              :  *                                                                  *
      18              :  * You should have received a copy of the GNU General Public License*
      19              :  * along with this program; if not, contact:                        *
      20              :  *                                                                  *
      21              :  * Free Software Foundation           Voice:  +1-617-542-5942       *
      22              :  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
      23              :  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
      24              :  *                                                                  *
      25              : \********************************************************************/
      26              : 
      27              : #include "qofinstance.h"
      28              : #include <config.h>
      29              : 
      30              : #include <platform.h>
      31              : #if PLATFORM(WINDOWS)
      32              : #include <windows.h>
      33              : #endif
      34              : 
      35              : #include <glib.h>
      36              : #include <glib/gi18n.h>
      37              : #include <stdlib.h>
      38              : #include <string.h>
      39              : #ifdef HAVE_SYS_TIME_H
      40              : # include <sys/time.h>
      41              : #endif
      42              : #include <time.h>
      43              : #ifdef HAVE_UNISTD_H
      44              : # include <unistd.h>
      45              : #endif
      46              : 
      47              : #include "qof.h"
      48              : #include "qofbook.h"
      49              : #include "Split.h"
      50              : #include "AccountP.hpp"
      51              : #include "Account.hpp"
      52              : #include "Scrub.h"
      53              : #include "TransactionP.hpp"
      54              : #include "TransLog.h"
      55              : #include "cap-gains.h"
      56              : #include "gnc-commodity.h"
      57              : #include "gnc-engine.h"
      58              : #include "gnc-lot.h"
      59              : #include "gnc-event.h"
      60              : #include "qofinstance-p.h"
      61              : 
      62              : const char *void_former_amt_str = "void-former-amount";
      63              : const char *void_former_val_str = "void-former-value";
      64              : 
      65              : /* This static indicates the debugging module that this .o belongs to.  */
      66              : static QofLogModule log_module = GNC_MOD_ENGINE;
      67              : 
      68              : /* KVP key values used for SX info stored Split's slots. */
      69              : #define GNC_SX_ID                    "sched-xaction"
      70              : #define GNC_SX_ACCOUNT               "account"
      71              : #define GNC_SX_CREDIT_FORMULA        "credit-formula"
      72              : #define GNC_SX_DEBIT_FORMULA         "debit-formula"
      73              : #define GNC_SX_CREDIT_NUMERIC        "credit-numeric"
      74              : #define GNC_SX_DEBIT_NUMERIC         "debit-numeric"
      75              : #define GNC_SX_SHARES                "shares"
      76              : 
      77              : enum
      78              : {
      79              :     PROP_0,
      80              :     PROP_TX,                    /* Table */
      81              :     PROP_ACCOUNT,               /* Table */
      82              :     PROP_MEMO,                  /* Table */
      83              :     PROP_ACTION,                /* Table */
      84              : //    PROP_RECONCILE_STATE,     /* Table */
      85              :     PROP_RECONCILE_DATE,        /* Table */
      86              :     PROP_VALUE,                 /* Table, in 2 fields */
      87              :     PROP_SX_ACCOUNT,            /* KVP */
      88              :     PROP_SX_CREDIT_FORMULA,     /* KVP */
      89              :     PROP_SX_CREDIT_NUMERIC,     /* KVP */
      90              :     PROP_SX_DEBIT_FORMULA,      /* KVP */
      91              :     PROP_SX_DEBIT_NUMERIC,      /* KVP */
      92              :     PROP_SX_SHARES,             /* KVP */
      93              :     PROP_LOT,                   /* KVP */
      94              :     PROP_ONLINE_ACCOUNT,        /* KVP */
      95              :     PROP_GAINS_SPLIT,           /* KVP */
      96              :     PROP_GAINS_SOURCE,          /* KVP */
      97              :     PROP_RUNTIME_0,
      98              :     PROP_AMOUNT,                /* Runtime */
      99              : 
     100              : };
     101              : 
     102              : static const char * split_type_normal = "normal";
     103              : static const char * split_type_stock_split = "stock-split";
     104              : static const char * split_online_id = "online_id";
     105              : 
     106              : /* GObject Initialization */
     107       133742 : G_DEFINE_TYPE(Split, gnc_split, QOF_TYPE_INSTANCE)
     108              : 
     109              : static void
     110        10122 : gnc_split_init(Split* split)
     111              : {
     112              :     /* fill in some sane defaults */
     113        10122 :     split->acc         = nullptr;
     114        10122 :     split->orig_acc    = nullptr;
     115        10122 :     split->parent      = nullptr;
     116        10122 :     split->lot         = nullptr;
     117              : 
     118        10122 :     split->action      = CACHE_INSERT("");
     119        10122 :     split->memo        = CACHE_INSERT("");
     120        10122 :     split->reconciled  = NREC;
     121        10122 :     split->amount      = gnc_numeric_zero();
     122        10122 :     split->value       = gnc_numeric_zero();
     123              : 
     124        10122 :     split->date_reconciled  = 0;
     125              : 
     126        10122 :     split->balance             = gnc_numeric_zero();
     127        10122 :     split->cleared_balance     = gnc_numeric_zero();
     128        10122 :     split->reconciled_balance  = gnc_numeric_zero();
     129        10122 :     split->noclosing_balance   = gnc_numeric_zero();
     130              : 
     131        10122 :     split->adjusted_amount     = gnc_numeric_zero();
     132        10122 :     split->split_type          = nullptr;
     133              : 
     134        10122 :     split->gains = GAINS_STATUS_UNKNOWN;
     135        10122 :     split->gains_split = nullptr;
     136        10122 : }
     137              : 
     138              : static void
     139        15576 : gnc_split_dispose(GObject *splitp)
     140              : {
     141        15576 :     G_OBJECT_CLASS(gnc_split_parent_class)->dispose(splitp);
     142        15576 : }
     143              : 
     144              : static void
     145         7834 : gnc_split_finalize(GObject* splitp)
     146              : {
     147         7834 :     G_OBJECT_CLASS(gnc_split_parent_class)->finalize(splitp);
     148         7834 : }
     149              : /* Note that g_value_set_object() refs the object, as does
     150              :  * g_object_get(). But g_object_get() only unrefs once when it disgorges
     151              :  * the object, leaving an unbalanced ref, which leaks. So instead of
     152              :  * using g_value_set_object(), use g_value_take_object() which doesn't
     153              :  * ref the object when used in get_property().
     154              :  */
     155              : static void
     156         1060 : gnc_split_get_property(GObject         *object,
     157              :                        guint            prop_id,
     158              :                        GValue          *value,
     159              :                        GParamSpec      *pspec)
     160              : {
     161              :     Split *split;
     162              :     Time64 t;
     163              : 
     164         1060 :     g_return_if_fail(GNC_IS_SPLIT(object));
     165              : 
     166         1060 :     split = GNC_SPLIT(object);
     167         1060 :     switch (prop_id)
     168              :     {
     169           23 :         case PROP_ACTION:
     170           23 :             g_value_set_string(value, split->action);
     171           23 :             break;
     172           24 :         case PROP_MEMO:
     173           24 :             g_value_set_string(value, split->memo);
     174           24 :             break;
     175           23 :         case PROP_VALUE:
     176           23 :             g_value_set_boxed(value, &split->value);
     177           23 :             break;
     178           23 :         case PROP_AMOUNT:
     179           23 :             g_value_set_boxed(value, &split->amount);
     180           23 :             break;
     181           23 :         case PROP_RECONCILE_DATE:
     182           23 :             t.t = split->date_reconciled;
     183           23 :             g_value_set_boxed(value, &t);
     184           23 :             break;
     185           23 :         case PROP_TX:
     186           23 :             g_value_take_object(value, split->parent);
     187           23 :             break;
     188           23 :         case PROP_ACCOUNT:
     189           23 :             g_value_take_object(value, split->acc);
     190           23 :             break;
     191            1 :         case PROP_LOT:
     192            1 :             g_value_take_object(value, split->lot);
     193            1 :             break;
     194           42 :         case PROP_SX_CREDIT_FORMULA:
     195           42 :             qof_instance_get_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_CREDIT_FORMULA);
     196           42 :             break;
     197           18 :         case PROP_SX_CREDIT_NUMERIC:
     198           18 :             qof_instance_get_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_CREDIT_NUMERIC);
     199           18 :             break;
     200           41 :         case PROP_SX_DEBIT_FORMULA:
     201           41 :             qof_instance_get_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_DEBIT_FORMULA);
     202           41 :             break;
     203           18 :         case PROP_SX_DEBIT_NUMERIC:
     204           18 :             qof_instance_get_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_DEBIT_NUMERIC);
     205           18 :             break;
     206           43 :         case PROP_SX_ACCOUNT:
     207           43 :             qof_instance_get_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_ACCOUNT);
     208           43 :             break;
     209            7 :         case PROP_SX_SHARES:
     210            7 :             qof_instance_get_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_SHARES);
     211            7 :             break;
     212            2 :         case PROP_ONLINE_ACCOUNT:
     213            2 :             qof_instance_get_kvp (QOF_INSTANCE (split), value, 1, "online_id");
     214            2 :             break;
     215          307 :         case PROP_GAINS_SPLIT:
     216          307 :             qof_instance_get_kvp (QOF_INSTANCE (split), value, 1, "gains-split");
     217          307 :             break;
     218          419 :         case PROP_GAINS_SOURCE:
     219          419 :             qof_instance_get_kvp (QOF_INSTANCE (split), value, 1, "gains-source");
     220          419 :             break;
     221            0 :         default:
     222            0 :             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
     223            0 :             break;
     224              :     }
     225              : }
     226              : 
     227              : static void
     228         1110 : gnc_split_set_property(GObject         *object,
     229              :                        guint            prop_id,
     230              :                        const GValue     *value,
     231              :                        GParamSpec      *pspec)
     232              : {
     233              :     Split *split;
     234              :     gnc_numeric* number;
     235              :     Time64 *t;
     236         1110 :     g_return_if_fail(GNC_IS_SPLIT(object));
     237              : 
     238         1110 :     split = GNC_SPLIT(object);
     239         1110 :     if (prop_id < PROP_RUNTIME_0 && split->parent != nullptr)
     240          760 :         g_assert (qof_instance_get_editlevel(split->parent));
     241              : 
     242         1110 :     switch (prop_id)
     243              :     {
     244           24 :         case PROP_ACTION:
     245           24 :             xaccSplitSetAction(split, g_value_get_string(value));
     246           24 :             break;
     247          230 :         case PROP_MEMO:
     248          230 :             xaccSplitSetMemo(split, g_value_get_string(value));
     249          230 :             break;
     250          230 :         case PROP_VALUE:
     251          230 :             number = static_cast<gnc_numeric*>(g_value_get_boxed(value));
     252          230 :             xaccSplitSetValue(split, *number);
     253          230 :             break;
     254          230 :         case PROP_AMOUNT:
     255          230 :             number = static_cast<gnc_numeric*>(g_value_get_boxed(value));
     256          230 :             xaccSplitSetAmount(split, *number);
     257          230 :             break;
     258           24 :         case PROP_RECONCILE_DATE:
     259           24 :             t = static_cast<Time64*>(g_value_get_boxed(value));
     260           24 :             xaccSplitSetDateReconciledSecs(split, t->t);
     261           24 :             break;
     262           24 :         case PROP_TX:
     263           24 :             xaccSplitSetParent(split, GNC_TRANSACTION(g_value_get_object(value)));
     264           24 :             break;
     265          230 :         case PROP_ACCOUNT:
     266          230 :             xaccSplitSetAccount(split, GNC_ACCOUNT(g_value_get_object(value)));
     267          230 :             break;
     268            1 :         case PROP_LOT:
     269            1 :             xaccSplitSetLot(split, GNC_LOT(g_value_get_object(value)));
     270            1 :             break;
     271           20 :         case PROP_SX_CREDIT_FORMULA:
     272           20 :             qof_instance_set_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_CREDIT_FORMULA);
     273           20 :             break;
     274           11 :         case PROP_SX_CREDIT_NUMERIC:
     275           11 :             qof_instance_set_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_CREDIT_NUMERIC);
     276           11 :             break;
     277           19 :         case PROP_SX_DEBIT_FORMULA:
     278           19 :             qof_instance_set_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_DEBIT_FORMULA);
     279           19 :             break;
     280           11 :         case PROP_SX_DEBIT_NUMERIC:
     281           11 :             qof_instance_set_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_DEBIT_NUMERIC);
     282           11 :             break;
     283           19 :         case PROP_SX_ACCOUNT:
     284           19 :             qof_instance_set_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_ACCOUNT);
     285           19 :             break;
     286            9 :         case PROP_SX_SHARES:
     287            9 :             qof_instance_set_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_SHARES);
     288            9 :             break;
     289           14 :         case PROP_ONLINE_ACCOUNT:
     290           14 :             qof_instance_set_kvp (QOF_INSTANCE (split), value, 1, "online_id");
     291           14 :             break;
     292            7 :         case PROP_GAINS_SPLIT:
     293            7 :             qof_instance_set_kvp (QOF_INSTANCE (split), value, 1, "gains-split");
     294            7 :             break;
     295            7 :         case PROP_GAINS_SOURCE:
     296            7 :             qof_instance_set_kvp (QOF_INSTANCE (split), value, 1, "gains-source");
     297            7 :             break;
     298            0 :         default:
     299            0 :             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
     300            0 :             break;
     301              :     }
     302              : }
     303              : 
     304              : static void
     305           43 : gnc_split_class_init(SplitClass* klass)
     306              : {
     307           43 :     GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
     308              : 
     309           43 :     gobject_class->dispose = gnc_split_dispose;
     310           43 :     gobject_class->finalize = gnc_split_finalize;
     311           43 :     gobject_class->set_property = gnc_split_set_property;
     312           43 :     gobject_class->get_property = gnc_split_get_property;
     313              : 
     314              :     g_object_class_install_property
     315           43 :         (gobject_class,
     316              :          PROP_ACTION,
     317              :          g_param_spec_string("action",
     318              :                              "Action",
     319              :                              "The action is an arbitrary string assigned "
     320              :                              "by the user.  It is intended to be a short "
     321              :                              "string that contains extra information about "
     322              :                              "this split.",
     323              :                              nullptr,
     324              :                              G_PARAM_READWRITE));
     325              : 
     326              :     g_object_class_install_property
     327           43 :         (gobject_class,
     328              :          PROP_MEMO,
     329              :          g_param_spec_string("memo",
     330              :                              "Memo",
     331              :                              "The action is an arbitrary string assigned "
     332              :                              "by the user.  It is intended to be a short "
     333              :                              "string that describes the purpose of "
     334              :                              "this split.",
     335              :                              nullptr,
     336              :                              G_PARAM_READWRITE));
     337              : 
     338              :     g_object_class_install_property
     339           43 :         (gobject_class,
     340              :          PROP_VALUE,
     341              :          g_param_spec_boxed("value",
     342              :                             "Split Value",
     343              :                             "The value for this split in the common currency. "
     344              :                             "The value and the amount provide enough information to "
     345              :                             "calculate a conversion rate.",
     346              :                             GNC_TYPE_NUMERIC,
     347              :                             G_PARAM_READWRITE));
     348              : 
     349              :     g_object_class_install_property
     350           43 :         (gobject_class,
     351              :          PROP_AMOUNT,
     352              :          g_param_spec_boxed("amount",
     353              :                             "Split Amount",
     354              :                             "The value for this split in the currency of its account. "
     355              :                             "The value and the amount provide enough information to "
     356              :                             "calculate a conversion rate.",
     357              :                             GNC_TYPE_NUMERIC,
     358              :                             G_PARAM_READWRITE));
     359              : 
     360              :     g_object_class_install_property
     361           43 :         (gobject_class,
     362              :          PROP_RECONCILE_DATE,
     363              :          g_param_spec_boxed("reconcile-date",
     364              :                             "Reconcile Date",
     365              :                             "The date this split was reconciled.",
     366              :                             GNC_TYPE_TIME64,
     367              :                             G_PARAM_READWRITE));
     368              : 
     369              :     g_object_class_install_property
     370           43 :         (gobject_class,
     371              :          PROP_TX,
     372              :          g_param_spec_object ("transaction",
     373              :                               "Transaction",
     374              :                               "The transaction that this split belongs to.",
     375              :                               GNC_TYPE_TRANSACTION,
     376              :                               G_PARAM_READWRITE));
     377              : 
     378              :     g_object_class_install_property
     379           43 :         (gobject_class,
     380              :          PROP_ACCOUNT,
     381              :          g_param_spec_object ("account",
     382              :                               "Account",
     383              :                               "The account that this split belongs to.",
     384              :                               GNC_TYPE_ACCOUNT,
     385              :                               G_PARAM_READWRITE));
     386              : 
     387              :     g_object_class_install_property
     388           43 :         (gobject_class,
     389              :          PROP_LOT,
     390              :          g_param_spec_object ("lot",
     391              :                               "Lot",
     392              :                               "The lot that this split belongs to.",
     393              :                               GNC_TYPE_LOT,
     394              :                               G_PARAM_READWRITE));
     395              : 
     396              :     g_object_class_install_property
     397           43 :         (gobject_class,
     398              :          PROP_SX_DEBIT_FORMULA,
     399              :          g_param_spec_string("sx-debit-formula",
     400              :                              "Schedule Transaction Debit Formula",
     401              :                              "The formula used to calculate the actual debit "
     402              :                              "amount when a real split is generated from this "
     403              :                              "SX split.",
     404              :                              nullptr,
     405              :                              G_PARAM_READWRITE));
     406              : 
     407              :     g_object_class_install_property
     408           43 :         (gobject_class,
     409              :          PROP_SX_DEBIT_NUMERIC,
     410              :          g_param_spec_boxed("sx-debit-numeric",
     411              :                             "Scheduled Transaction Debit Numeric",
     412              :                             "Numeric value to plug into the Debit Formula when a "
     413              :                             "real split is generated from this SX split.",
     414              :                             GNC_TYPE_NUMERIC,
     415              :                             G_PARAM_READWRITE));
     416              : 
     417              :     g_object_class_install_property
     418           43 :         (gobject_class,
     419              :          PROP_SX_CREDIT_FORMULA,
     420              :          g_param_spec_string("sx-credit-formula",
     421              :                              "Schedule Transaction Credit Formula",
     422              :                              "The formula used to calculate the actual credit "
     423              :                              "amount when a real split is generated from this "
     424              :                              "SX split.",
     425              :                              nullptr,
     426              :                              G_PARAM_READWRITE));
     427              : 
     428              :     g_object_class_install_property
     429           43 :         (gobject_class,
     430              :          PROP_SX_CREDIT_NUMERIC,
     431              :          g_param_spec_boxed("sx-credit-numeric",
     432              :                             "Scheduled Transaction Credit Numeric",
     433              :                             "Numeric value to plug into the Credit Formula when a "
     434              :                             "real split is generated from this SX split.",
     435              :                             GNC_TYPE_NUMERIC,
     436              :                             G_PARAM_READWRITE));
     437              : /* FIXME: PROP_SX_SHARES should be stored as a gnc_numeric, but the function
     438              :  * which uses it, gnc_template_register_save_shares_cell, stores a
     439              :  * phony string. This is maintained until backwards compatibility can
     440              :  * be established.
     441              :  */
     442              :     g_object_class_install_property
     443           43 :         (gobject_class,
     444              :          PROP_SX_SHARES,
     445              :          g_param_spec_string("sx-shares",
     446              :                              "Scheduled Transaction Shares",
     447              :                              "Numeric value of shares to insert in a new split when "
     448              :                              "it's generated from this SX split.",
     449              :                              nullptr,
     450              :                              G_PARAM_READWRITE));
     451              : 
     452              :     g_object_class_install_property
     453           43 :         (gobject_class,
     454              :          PROP_SX_ACCOUNT,
     455              :          g_param_spec_boxed("sx-account",
     456              :                             "Scheduled Transaction Account",
     457              :                             "The target account for a scheduled transaction split.",
     458              :                             GNC_TYPE_GUID,
     459              :                             G_PARAM_READWRITE));
     460              : 
     461              :     g_object_class_install_property
     462           43 :         (gobject_class,
     463              :          PROP_ONLINE_ACCOUNT,
     464              :          g_param_spec_string ("online-id",
     465              :                               "Online Account ID",
     466              :                               "The online account which corresponds to this "
     467              :                               "account for OFX/HCBI import",
     468              :                               nullptr,
     469              :                               G_PARAM_READWRITE));
     470              : 
     471              :     g_object_class_install_property
     472           43 :         (gobject_class,
     473              :          PROP_GAINS_SPLIT,
     474              :          g_param_spec_boxed ("gains-split",
     475              :                              "Gains Split",
     476              :                              "The capital gains split associated with this "
     477              :                              "split when this split represents the proceeds "
     478              :                              "from the sale of a commodity inside a Lot.",
     479              :                              GNC_TYPE_GUID,
     480              :                              G_PARAM_READWRITE));
     481              : 
     482              :     g_object_class_install_property
     483           43 :         (gobject_class,
     484              :          PROP_GAINS_SOURCE,
     485              :          g_param_spec_boxed ("gains-source",
     486              :                              "Gains Source",
     487              :                              "The source split for which this split this is "
     488              :                              "the gains split. ",
     489              :                              GNC_TYPE_GUID,
     490              :                              G_PARAM_READWRITE));
     491           43 : }
     492              : 
     493              : /********************************************************************\
     494              :  * xaccInitSplit
     495              :  * Initialize a Split structure
     496              : \********************************************************************/
     497              : 
     498              : static void
     499         7291 : xaccInitSplit(Split * split, QofBook *book)
     500              : {
     501         7291 :     qof_instance_init_data(&split->inst, GNC_ID_SPLIT, book);
     502         7291 : }
     503              : 
     504              : void
     505            0 : xaccSplitReinit(Split * split)
     506              : {
     507              :     /* fill in some sane defaults */
     508            0 :     split->acc         = nullptr;
     509            0 :     split->orig_acc    = nullptr;
     510            0 :     split->parent      = nullptr;
     511            0 :     split->lot         = nullptr;
     512              : 
     513            0 :     CACHE_REPLACE(split->action, "");
     514            0 :     CACHE_REPLACE(split->memo, "");
     515            0 :     split->reconciled  = NREC;
     516            0 :     split->amount      = gnc_numeric_zero();
     517            0 :     split->value       = gnc_numeric_zero();
     518              : 
     519            0 :     split->date_reconciled  = 0;
     520              : 
     521            0 :     split->balance             = gnc_numeric_zero();
     522            0 :     split->cleared_balance     = gnc_numeric_zero();
     523            0 :     split->reconciled_balance  = gnc_numeric_zero();
     524            0 :     split->noclosing_balance   = gnc_numeric_zero();
     525              : 
     526            0 :     split->adjusted_amount     = gnc_numeric_zero();
     527            0 :     split->split_type          = nullptr;
     528              : 
     529            0 :     qof_instance_set_idata(split, 0);
     530              : 
     531            0 :     split->gains = GAINS_STATUS_UNKNOWN;
     532            0 :     split->gains_split = nullptr;
     533            0 : }
     534              : 
     535              : /********************************************************************\
     536              : \********************************************************************/
     537              : 
     538              : Split *
     539         7291 : xaccMallocSplit(QofBook *book)
     540              : {
     541              :     Split *split;
     542         7291 :     g_return_val_if_fail (book, nullptr);
     543              : 
     544         7291 :     split = GNC_SPLIT(g_object_new (GNC_TYPE_SPLIT, nullptr));
     545         7291 :     xaccInitSplit (split, book);
     546              : 
     547         7291 :     return split;
     548              : }
     549              : 
     550              : /********************************************************************\
     551              : \********************************************************************/
     552              : /* This routine is not exposed externally, since it does weird things,
     553              :  * like not really setting up the parent account correctly, and ditto
     554              :  * the parent transaction.  This routine is prone to programmer error
     555              :  * if not used correctly.  It is used only by the edit-rollback code.
     556              :  * Don't get duped!
     557              :  */
     558              : 
     559              : Split *
     560         2805 : xaccDupeSplit (const Split *s)
     561              : {
     562         2805 :     Split *split = GNC_SPLIT(g_object_new (GNC_TYPE_SPLIT, nullptr));
     563              : 
     564              :     /* Trash the entity table. We don't want to mistake the cloned
     565              :      * splits as something official.  If we ever use this split, we'll
     566              :      * have to fix this up.
     567              :      */
     568         2805 :     split->inst.e_type = nullptr;
     569         2805 :     qof_instance_copy_guid(split, s);
     570         2805 :     qof_instance_copy_book(split, s);
     571              : 
     572         2805 :     split->parent = s->parent;
     573         2805 :     split->acc = s->acc;
     574         2805 :     split->orig_acc = s->orig_acc;
     575         2805 :     split->lot = s->lot;
     576              : 
     577         2805 :     CACHE_REPLACE(split->memo, s->memo);
     578         2805 :     CACHE_REPLACE(split->action, s->action);
     579              : 
     580         2805 :     qof_instance_copy_kvp (QOF_INSTANCE (split), QOF_INSTANCE (s));
     581              : 
     582         2805 :     split->reconciled = s->reconciled;
     583         2805 :     split->date_reconciled = s->date_reconciled;
     584              : 
     585         2805 :     split->value = s->value;
     586         2805 :     split->amount = s->amount;
     587              : 
     588              :     /* no need to futz with the balances;  these get wiped each time ...
     589              :      * split->balance             = s->balance;
     590              :      * split->cleared_balance     = s->cleared_balance;
     591              :      * split->reconciled_balance  = s->reconciled_balance;
     592              :      */
     593              : 
     594         2805 :     return split;
     595              : }
     596              : 
     597              : Split *
     598           23 : xaccSplitCloneNoKvp (const Split *s)
     599              : {
     600           23 :     Split *split = GNC_SPLIT(g_object_new (GNC_TYPE_SPLIT, nullptr));
     601              : 
     602           23 :     split->parent              = nullptr;
     603           23 :     split->memo                = CACHE_INSERT(s->memo);
     604           23 :     split->action              = CACHE_INSERT(s->action);
     605           23 :     split->reconciled          = s->reconciled;
     606           23 :     split->date_reconciled     = s->date_reconciled;
     607           23 :     split->value               = s->value;
     608           23 :     split->amount              = s->amount;
     609           23 :     split->balance             = s->balance;
     610           23 :     split->cleared_balance     = s->cleared_balance;
     611           23 :     split->reconciled_balance  = s->reconciled_balance;
     612           23 :     split->noclosing_balance   = s->noclosing_balance;
     613           23 :     split->adjusted_amount     = s->adjusted_amount;
     614           23 :     split->split_type          = s->split_type;
     615              : 
     616           23 :     split->gains = GAINS_STATUS_UNKNOWN;
     617           23 :     split->gains_split = nullptr;
     618              : 
     619           23 :     qof_instance_init_data(&split->inst, GNC_ID_SPLIT,
     620              :                            qof_instance_get_book(s));
     621           23 :     xaccAccountInsertSplit(s->acc, split);
     622           23 :     if (s->lot)
     623              :     {
     624              :         /* CHECKME: Is this right? */
     625            2 :         gnc_lot_add_split(s->lot, split);
     626              :     }
     627           23 :     return split;
     628              : }
     629              : 
     630              : void
     631           13 : xaccSplitCopyKvp (const Split *from, Split *to)
     632              : {
     633           13 :     qof_instance_copy_kvp (QOF_INSTANCE (to), QOF_INSTANCE (from));
     634              :     /* But not the online-id */
     635           13 :     qof_instance_set (QOF_INSTANCE (to), "online-id", nullptr, nullptr);
     636           13 : }
     637              : 
     638              : /*################## Added for Reg2 #################*/
     639              : 
     640              : /* This is really a helper for xaccTransCopyOnto. It doesn't reparent
     641              :    the 'to' split to from's transaction, because xaccTransCopyOnto is
     642              :    responsible for parenting the split to the correct transaction.
     643              :    Also, from's parent transaction may not even be a valid
     644              :    transaction, so this function may not modify anything about 'from'
     645              :    or from's transaction.
     646              : */
     647              : void
     648            4 : xaccSplitCopyOnto(const Split *from_split, Split *to_split)
     649              : {
     650            4 :     if (!from_split || !to_split) return;
     651            4 :     xaccTransBeginEdit (to_split->parent);
     652              : 
     653            4 :     xaccSplitSetMemo(to_split, xaccSplitGetMemo(from_split));
     654            4 :     xaccSplitSetAction(to_split, xaccSplitGetAction(from_split));
     655            4 :     xaccSplitSetAmount(to_split, xaccSplitGetAmount(from_split));
     656            4 :     xaccSplitSetValue(to_split, xaccSplitGetValue(from_split));
     657              :     /* Setting the account is okay here because, even though the from
     658              :        split might not really belong to the account it claims to,
     659              :        setting the account won't cause any event involving from. */
     660            4 :     xaccSplitSetAccount(to_split, xaccSplitGetAccount(from_split));
     661              :     /* N.B. Don't set parent. */
     662              : 
     663            4 :     qof_instance_set_dirty(QOF_INSTANCE(to_split));
     664            4 :     xaccTransCommitEdit(to_split->parent);
     665              : }
     666              : 
     667              : /*################## Added for Reg2 #################*/
     668              : 
     669              : 
     670              : #ifdef DUMP_FUNCTIONS
     671              : void
     672              : xaccSplitDump (const Split *split, const char *tag)
     673              : {
     674              :     char datebuff[MAX_DATE_LENGTH + 1];
     675              :     memset (datebuff, 0, sizeof(datebuff));
     676              :     qof_print_date_buff (datebuff, MAX_DATE_LENGTH, split->date_reconciled);
     677              :     printf("  %s Split %p", tag, split);
     678              :     printf("    Book:     %p\n", qof_instance_get_book(split));
     679              :     printf("    Account:  %p (%s)\n", split->acc,
     680              :            split->acc ? xaccAccountGetName(split->acc) : "");
     681              :     printf("    Commod:   %s\n",
     682              :            split->acc ?
     683              :            gnc_commodity_get_printname(xaccAccountGetCommodity(split->acc))
     684              :            : "");
     685              :     printf("    Lot:      %p\n", split->lot);
     686              :     printf("    Parent:   %p\n", split->parent);
     687              :     printf("    Gains:    %p\n", split->gains_split);
     688              :     printf("    Memo:     %s\n", split->memo ? split->memo : "(null)");
     689              :     printf("    Action:   %s\n", split->action ? split->action : "(null)");
     690              :     printf("    KVP Data: %s\n", qof_instance_kvp_as_string (QOF_INSTANCE (split)));
     691              :     printf("    Recncld:  %c (date %s)\n", split->reconciled, datebuff);
     692              : 
     693              :     printf("    Value:    %s\n", gnc_numeric_to_string(split->value));
     694              :     printf("    Amount:   %s\n", gnc_numeric_to_string(split->amount));
     695              :     printf("    Balance:  %s\n", gnc_numeric_to_string(split->balance));
     696              :     printf("    CBalance: %s\n", gnc_numeric_to_string(split->cleared_balance));
     697              :     printf("    RBalance: %s\n",
     698              :            gnc_numeric_to_string(split->reconciled_balance));
     699              :     printf("    NoClose:  %s\n", gnc_numeric_to_string(split->noclosing_balance));
     700              :     printf("    AdjAmt:   %s\n", gnc_numeric_to_string(split->adjusted_amount));
     701              :     printf("    Type:     %s\n", split->split_type ? split->split_type : "(null)");
     702              :     printf("    idata:    %x\n", qof_instance_get_idata(split));
     703              : }
     704              : #endif
     705              : 
     706              : /********************************************************************\
     707              : \********************************************************************/
     708              : static void
     709         4940 : do_destroy (QofInstance *inst)
     710              : {
     711         4940 :     xaccFreeSplit (GNC_SPLIT (inst));
     712         4940 : }
     713              : 
     714              : void
     715         7741 : xaccFreeSplit (Split *split)
     716              : {
     717         7741 :     if (!split) return;
     718              : 
     719              :     /* Debug double-free's */
     720         7741 :     if (((char *) 1) == split->memo)
     721              :     {
     722            0 :         PERR ("double-free %p", split);
     723            0 :         return;
     724              :     }
     725         7741 :     CACHE_REMOVE(split->memo);
     726         7741 :     CACHE_REMOVE(split->action);
     727              : 
     728         7741 :     if (split->inst.e_type) /* Don't do this for dupe splits. */
     729              :     {
     730              :         /* gnc_lot_remove_split needs the account, so do it first. */
     731         4946 :         if (GNC_IS_LOT (split->lot) && !qof_instance_get_destroying (QOF_INSTANCE (split->lot)))
     732            0 :             gnc_lot_remove_split (split->lot, split);
     733         4946 :         if (GNC_IS_ACCOUNT (split->acc)
     734         4946 :             && !qof_instance_get_destroying (QOF_INSTANCE (split->acc)))
     735              :         {
     736         4942 :             gnc_account_remove_split (split->acc, split);
     737              :             /* gnc_float_split_to_split generates a qof_event_gen via
     738              :              * xaccAccountCommitEdit even though it doesn't touch the
     739              :              * account. That causes QofQueryViews to notice the split
     740              :              * even though it isn't added to the account. We need a
     741              :              * countervailing event so that they'll notice it's not
     742              :              * there any more.
     743              :              */
     744         4942 :             qof_event_gen(&split->acc->inst, QOF_EVENT_MODIFY, nullptr);
     745              :         }
     746              :         /* We should do the same for split->parent but we might be getting
     747              :          * called from xaccFreeTransaction and that would cause trouble.
     748              :          */
     749              :     }
     750              : 
     751              :     /* Just in case someone looks up freed memory ... */
     752         7741 :     split->memo        = (char *) 1;
     753         7741 :     split->action      = nullptr;
     754         7741 :     split->reconciled  = NREC;
     755         7741 :     split->amount      = gnc_numeric_zero();
     756         7741 :     split->value       = gnc_numeric_zero();
     757         7741 :     split->parent      = nullptr;
     758         7741 :     split->lot         = nullptr;
     759         7741 :     split->acc         = nullptr;
     760         7741 :     split->orig_acc    = nullptr;
     761              : 
     762         7741 :     split->balance            = gnc_numeric_zero();
     763         7741 :     split->cleared_balance    = gnc_numeric_zero();
     764         7741 :     split->reconciled_balance = gnc_numeric_zero();
     765         7741 :     split->noclosing_balance  = gnc_numeric_zero();
     766         7741 :     split->adjusted_amount    = gnc_numeric_zero();
     767         7741 :     split->split_type         = nullptr;
     768              : 
     769         7741 :     split->date_reconciled = 0;
     770         7741 :     G_OBJECT_CLASS (QOF_INSTANCE_GET_CLASS (&split->inst))->dispose(G_OBJECT (split));
     771              : 
     772         7741 :     if (split->gains_split)
     773              :     {
     774            6 :         Split *other = xaccSplitGetOtherSplit(split->gains_split);
     775            6 :         split->gains_split->gains_split = nullptr;
     776            6 :         if (other)
     777            6 :           other->gains_split = nullptr;
     778              :     }
     779              : 
     780         7741 :     g_object_unref(split);
     781              : }
     782              : 
     783        31497 : void mark_split (Split *s)
     784              : {
     785        31497 :     if (s->acc)
     786        22043 :         g_object_set(s->acc, "sort-dirty", TRUE, "balance-dirty", TRUE, nullptr);
     787              : 
     788              :     /* set dirty flag on lot too. */
     789        31497 :     if (s->lot)
     790          232 :         gnc_lot_set_closed_unknown(s->lot);
     791        31497 : }
     792              : 
     793              : /*
     794              :  * Helper routine for xaccSplitEqual.
     795              :  */
     796              : static gboolean
     797          795 : xaccSplitEqualCheckBal (const char *tag, gnc_numeric a, gnc_numeric b)
     798              : {
     799              :     char *str_a, *str_b;
     800              : 
     801          795 :     if (gnc_numeric_equal (a, b))
     802          789 :         return TRUE;
     803              : 
     804            6 :     str_a = gnc_numeric_to_string (a);
     805            6 :     str_b = gnc_numeric_to_string (b);
     806              : 
     807            6 :     PINFO ("%sbalances differ: %s vs %s", tag, str_a, str_b);
     808              : 
     809            6 :     g_free (str_a);
     810            6 :     g_free (str_b);
     811              : 
     812            6 :     return FALSE;
     813              : }
     814              : 
     815              : /********************************************************************
     816              :  * xaccSplitEqual
     817              :  ********************************************************************/
     818              : gboolean
     819          189 : xaccSplitEqual(const Split *sa, const Split *sb,
     820              :                gboolean check_guids,
     821              :                gboolean check_balances,
     822              :                gboolean check_txn_splits)
     823              : {
     824              :     gboolean same_book;
     825              : 
     826          189 :     if (!sa && !sb) return TRUE; /* Arguable. FALSE is better, methinks */
     827              : 
     828          189 :     if (!sa || !sb)
     829              :     {
     830            2 :         PINFO ("one is nullptr");
     831            2 :         return FALSE;
     832              :     }
     833              : 
     834          187 :     if (sa == sb) return TRUE;
     835              : 
     836          183 :     same_book = qof_instance_get_book(QOF_INSTANCE(sa)) == qof_instance_get_book(QOF_INSTANCE(sb));
     837              : 
     838          183 :     if (check_guids)
     839              :     {
     840          156 :         if (qof_instance_guid_compare(sa, sb) != 0)
     841              :         {
     842            3 :             PINFO ("GUIDs differ");
     843            3 :             return FALSE;
     844              :         }
     845              :     }
     846              : 
     847              :     /* If the same book, since these strings are cached we can just use pointer equality */
     848          180 :     if ((same_book && sa->memo != sb->memo) || (!same_book && g_strcmp0(sa->memo, sb->memo) != 0))
     849              :     {
     850            1 :         PINFO ("memos differ: (%p)%s vs (%p)%s",
     851              :                sa->memo, sa->memo, sb->memo, sb->memo);
     852            1 :         return FALSE;
     853              :     }
     854              : 
     855          179 :     if ((same_book && sa->action != sb->action) || (!same_book && g_strcmp0(sa->action, sb->action) != 0))
     856              :     {
     857            2 :         PINFO ("actions differ: %s vs %s", sa->action, sb->action);
     858            2 :         return FALSE;
     859              :     }
     860              : 
     861          177 :     if (qof_instance_compare_kvp (QOF_INSTANCE (sa), QOF_INSTANCE (sb)) != 0)
     862              :     {
     863              :         char *frame_a;
     864              :         char *frame_b;
     865              : 
     866            0 :         frame_a = qof_instance_kvp_as_string (QOF_INSTANCE (sa));
     867            0 :         frame_b = qof_instance_kvp_as_string (QOF_INSTANCE (sb));
     868              : 
     869            0 :         PINFO ("kvp frames differ:\n%s\n\nvs\n\n%s", frame_a, frame_b);
     870              : 
     871            0 :         g_free (frame_a);
     872            0 :         g_free (frame_b);
     873              : 
     874            0 :         return FALSE;
     875              :     }
     876              : 
     877          177 :     if (sa->reconciled != sb->reconciled)
     878              :     {
     879            0 :         PINFO ("reconcile flags differ: %c vs %c", sa->reconciled, sb->reconciled);
     880            0 :         return FALSE;
     881              :     }
     882              : 
     883          177 :     if (sa->date_reconciled != sb->date_reconciled)
     884              :     {
     885            0 :         PINFO ("reconciled date differs");
     886            0 :         return FALSE;
     887              :     }
     888              : 
     889          177 :     if (!gnc_numeric_eq(xaccSplitGetAmount (sa), xaccSplitGetAmount (sb)))
     890              :     {
     891              :         char *str_a;
     892              :         char *str_b;
     893              : 
     894            0 :         str_a = gnc_numeric_to_string (xaccSplitGetAmount (sa));
     895            0 :         str_b = gnc_numeric_to_string (xaccSplitGetAmount (sb));
     896              : 
     897            0 :         PINFO ("amounts differ: %s vs %s", str_a, str_b);
     898              : 
     899            0 :         g_free (str_a);
     900            0 :         g_free (str_b);
     901              : 
     902            0 :         return FALSE;
     903              :     }
     904              : 
     905          177 :     if (!gnc_numeric_eq(xaccSplitGetValue (sa), xaccSplitGetValue (sb)))
     906              :     {
     907              :         char *str_a;
     908              :         char *str_b;
     909              : 
     910            0 :         str_a = gnc_numeric_to_string (xaccSplitGetValue (sa));
     911            0 :         str_b = gnc_numeric_to_string (xaccSplitGetValue (sb));
     912              : 
     913            0 :         PINFO ("values differ: %s vs %s", str_a, str_b);
     914              : 
     915            0 :         g_free (str_a);
     916            0 :         g_free (str_b);
     917              : 
     918            0 :         return FALSE;
     919              :     }
     920              : 
     921          177 :     if (check_balances)
     922              :     {
     923          162 :         if (!xaccSplitEqualCheckBal ("", sa->balance, sb->balance))
     924            3 :             return FALSE;
     925          159 :         if (!xaccSplitEqualCheckBal ("cleared ", sa->cleared_balance,
     926              :                                      sb->cleared_balance))
     927            1 :             return FALSE;
     928          158 :         if (!xaccSplitEqualCheckBal ("reconciled ", sa->reconciled_balance,
     929              :                                      sb->reconciled_balance))
     930            1 :             return FALSE;
     931          157 :         if (!xaccSplitEqualCheckBal ("noclosing ", sa->noclosing_balance,
     932              :                                      sb->noclosing_balance))
     933            0 :             return FALSE;
     934          157 :         if (!xaccSplitEqualCheckBal ("adjusted ", sa->adjusted_amount,
     935              :                                      sb->adjusted_amount))
     936            0 :             return FALSE;
     937              :     }
     938              : 
     939          172 :     if (!xaccTransEqual(sa->parent, sb->parent, check_guids, check_txn_splits,
     940              :                         check_balances, FALSE))
     941              :     {
     942            1 :         PINFO ("transactions differ");
     943            1 :         return FALSE;
     944              :     }
     945              : 
     946          171 :     return TRUE;
     947              : }
     948              : 
     949              : 
     950              : 
     951              : /********************************************************************
     952              :  * Account funcs
     953              :  ********************************************************************/
     954              : 
     955              : Account *
     956       124695 : xaccSplitGetAccount (const Split *s)
     957              : {
     958       124695 :     return s ? s->acc : nullptr;
     959              : }
     960              : 
     961              : void
     962         7262 : xaccSplitSetAccount (Split *s, Account *acc)
     963              : {
     964              :     Transaction *trans;
     965              : 
     966         7262 :     g_return_if_fail(s && acc);
     967         7262 :     g_return_if_fail(qof_instance_books_equal(acc, s));
     968              : 
     969         7262 :     trans = s->parent;
     970         7262 :     if (trans)
     971         5143 :         xaccTransBeginEdit(trans);
     972              : 
     973         7262 :     s->acc = acc;
     974         7262 :     qof_instance_set_dirty(QOF_INSTANCE(s));
     975              : 
     976         7262 :     if (trans)
     977         5143 :         xaccTransCommitEdit(trans);
     978              : }
     979              : 
     980            0 : static void commit_err (QofInstance *inst, QofBackendError errcode)
     981              : {
     982            0 :     PERR("commit error: %d", errcode);
     983            0 :     gnc_engine_signal_commit_error( errcode );
     984            0 : }
     985              : 
     986              : /* An engine-private helper for completing xaccTransCommitEdit(). */
     987              : void
     988        13119 : xaccSplitCommitEdit(Split *s)
     989              : {
     990        13119 :     Account *acc = nullptr;
     991        13119 :     Account *orig_acc = nullptr;
     992              : 
     993        13119 :     g_return_if_fail(s);
     994        13119 :     if (!qof_instance_is_dirty(QOF_INSTANCE(s)))
     995            0 :         return;
     996              : 
     997        13119 :     orig_acc = s->orig_acc;
     998              : 
     999        13119 :     if (GNC_IS_ACCOUNT(s->acc))
    1000        13101 :         acc = s->acc;
    1001              : 
    1002              :     /* Remove from lot (but only if it hasn't been moved to
    1003              :        new lot already) */
    1004        13119 :     if (s->lot && (gnc_lot_get_account(s->lot) != acc || qof_instance_get_destroying(s)))
    1005           11 :         gnc_lot_remove_split (s->lot, s);
    1006              : 
    1007              :     /* Possibly remove the split from the original account... */
    1008        13119 :     if (orig_acc && (orig_acc != acc || qof_instance_get_destroying(s)))
    1009              :     {
    1010         5058 :         if (!gnc_account_remove_split(orig_acc, s))
    1011              :         {
    1012            2 :             PERR("Account lost track of moved or deleted split.");
    1013              :         }
    1014              :     }
    1015              : 
    1016              :     /* ... and insert it into the new account if needed */
    1017        13119 :     if (acc && (orig_acc != acc) && !qof_instance_get_destroying(s))
    1018              :     {
    1019         7080 :         if (gnc_account_insert_split(acc, s))
    1020              :         {
    1021              :             /* If the split's lot belonged to some other account, we
    1022              :                leave it so. */
    1023         7079 :             if (s->lot && (nullptr == gnc_lot_get_account(s->lot)))
    1024            0 :                 xaccAccountInsertLot (acc, s->lot);
    1025              :         }
    1026              :         else
    1027              :         {
    1028            1 :             PERR("Account grabbed split prematurely.");
    1029              :         }
    1030         7080 :         xaccSplitSetAmount(s, xaccSplitGetAmount(s));
    1031              :     }
    1032              : 
    1033        13119 :     if (s->parent != s->orig_parent)
    1034              :     {
    1035              :         //FIXME: find better event
    1036         6967 :         if (s->orig_parent)
    1037            3 :             qof_event_gen(&s->orig_parent->inst, QOF_EVENT_MODIFY,
    1038              :                           nullptr);
    1039              :     }
    1040        13119 :     if (s->lot)
    1041              :     {
    1042              :         /* A change of value/amnt affects gains display, etc. */
    1043          299 :         qof_event_gen (QOF_INSTANCE(s->lot), QOF_EVENT_MODIFY, nullptr);
    1044              :     }
    1045              : 
    1046              :     /* Important: we save off the original parent transaction and account
    1047              :        so that when we commit, we can generate signals for both the
    1048              :        original and new transactions, for the _next_ begin/commit cycle. */
    1049        13119 :     s->orig_acc = s->acc;
    1050        13119 :     s->orig_parent = s->parent;
    1051        13119 :     if (!qof_commit_edit_part2(QOF_INSTANCE(s), commit_err, nullptr, do_destroy))
    1052            0 :         return;
    1053              : 
    1054        13119 :     if (acc)
    1055              :     {
    1056        13101 :         g_object_set(acc, "sort-dirty", TRUE, "balance-dirty", TRUE, nullptr);
    1057        13101 :         xaccAccountRecomputeBalance(acc);
    1058              :     }
    1059              : }
    1060              : 
    1061              : /* An engine-private helper for completing xaccTransRollbackEdit(). */
    1062              : void
    1063            7 : xaccSplitRollbackEdit(Split *s)
    1064              : {
    1065              : 
    1066              :     /* Don't use setters because we want to allow nullptr.  This is legit
    1067              :        only because we don't emit events for changing accounts until
    1068              :        the final commit. */
    1069            7 :     if (s->acc != s->orig_acc)
    1070            3 :         s->acc = s->orig_acc;
    1071              : 
    1072              :     /* Undestroy if needed */
    1073            7 :     if (qof_instance_get_destroying(s) && s->parent)
    1074              :     {
    1075              :         GncEventData ed;
    1076            1 :         qof_instance_set_destroying(s, FALSE);
    1077            1 :         ed.node = s;
    1078            1 :         ed.idx = -1; /* unused */
    1079            1 :         qof_event_gen(&s->parent->inst, GNC_EVENT_ITEM_ADDED, &ed);
    1080              :     }
    1081              : 
    1082              :     /* But for the parent trans, we want the intermediate events, so
    1083              :        we use the setter. */
    1084            7 :     xaccSplitSetParent(s, s->orig_parent);
    1085            7 : }
    1086              : 
    1087              : /********************************************************************\
    1088              : \********************************************************************/
    1089              : 
    1090              : Split *
    1091           60 : xaccSplitLookup (const GncGUID *guid, QofBook *book)
    1092              : {
    1093              :     QofCollection *col;
    1094           60 :     if (!guid || !book) return nullptr;
    1095           58 :     col = qof_book_get_collection (book, GNC_ID_SPLIT);
    1096           58 :     return (Split *) qof_collection_lookup_entity (col, guid);
    1097              : }
    1098              : 
    1099              : /********************************************************************\
    1100              : \********************************************************************/
    1101              : /* Routines for marking splits dirty, and for sending out change
    1102              :  * events.  Note that we can't just mark-n-generate-event in one
    1103              :  * step, since sometimes we need to mark things up before its suitable
    1104              :  * to send out a change event.
    1105              :  */
    1106              : 
    1107              : /* CHECKME: This function modifies the Split without dirtying or
    1108              :    checking its parent.  Is that correct? */
    1109              : void
    1110          440 : xaccSplitDetermineGainStatus (Split *split)
    1111              : {
    1112              :     Split *other;
    1113              : 
    1114          440 :     if (GAINS_STATUS_UNKNOWN != split->gains) return;
    1115              : 
    1116              :     /* A gains source has a forward "gains-split" link to the split that
    1117              :      * records its gains. */
    1118          307 :     other = xaccSplitGetCapGainsSplit (split);
    1119          307 :     if (other)
    1120              :     {
    1121            0 :         split->gains = GAINS_STATUS_A_VDIRTY | GAINS_STATUS_DATE_DIRTY;
    1122            0 :         split->gains_split = other;
    1123              :         /* Resolve the gains-recording split too, so it reuses this link
    1124              :          * rather than being re-created if it is processed first. */
    1125            0 :         if (GAINS_STATUS_UNKNOWN == other->gains)
    1126              :         {
    1127            0 :             other->gains = GAINS_STATUS_GAINS;
    1128            0 :             other->gains_split = split;
    1129              :         }
    1130            0 :         return;
    1131              :     }
    1132              : 
    1133              :     /* A gains-recording split has a backward "gains-source" link to the
    1134              :      * split whose gains it records. */
    1135          307 :     other = xaccSplitGetGainsSourceSplit (split);
    1136          307 :     if (!other)
    1137              :     {
    1138              :         // CHECKME: We leave split->gains_split alone.  Is that correct?
    1139          305 :         split->gains = GAINS_STATUS_A_VDIRTY | GAINS_STATUS_DATE_DIRTY;
    1140              :     }
    1141              :     else
    1142              :     {
    1143            2 :         split->gains = GAINS_STATUS_GAINS;
    1144            2 :         split->gains_split = other;
    1145              :         /* Resolve the source too, so it reuses this split rather than
    1146              :          * creating a duplicate if the source is processed afterward. */
    1147            2 :         if (GAINS_STATUS_UNKNOWN == other->gains)
    1148              :         {
    1149            2 :             other->gains = GAINS_STATUS_A_VDIRTY | GAINS_STATUS_DATE_DIRTY;
    1150            2 :             other->gains_split = split;
    1151              :         }
    1152              :     }
    1153              : }
    1154              : 
    1155              : /********************************************************************\
    1156              : \********************************************************************/
    1157              : 
    1158              : static inline int
    1159        15052 : get_currency_denom(const Split * s)
    1160              : {
    1161        15052 :     if (!(s && s->parent && s->parent->common_currency))
    1162              :     {
    1163         2863 :         return GNC_DENOM_AUTO;
    1164              :     }
    1165              :     else
    1166              :     {
    1167        12189 :         return gnc_commodity_get_fraction (s->parent->common_currency);
    1168              :     }
    1169              : }
    1170              : 
    1171              : static inline int
    1172        12834 : get_commodity_denom(const Split * s)
    1173              : {
    1174        12834 :     if (!(s && s->acc))
    1175              :     {
    1176            2 :         return GNC_DENOM_AUTO;
    1177              :     }
    1178              :     else
    1179              :     {
    1180        12832 :         return xaccAccountGetCommoditySCU(s->acc);
    1181              :     }
    1182              : }
    1183              : 
    1184              : /********************************************************************\
    1185              : \********************************************************************/
    1186              : 
    1187              : void
    1188            2 : xaccSplitSetSharePriceAndAmount (Split *s, gnc_numeric price, gnc_numeric amt)
    1189              : {
    1190            2 :     if (!s) return;
    1191            2 :     ENTER (" ");
    1192            2 :     xaccTransBeginEdit (s->parent);
    1193              : 
    1194            2 :     s->amount = gnc_numeric_convert(amt, get_commodity_denom(s),
    1195              :                                     GNC_HOW_RND_ROUND_HALF_UP);
    1196            2 :     s->value  = gnc_numeric_mul(s->amount, price,
    1197            2 :                                 get_currency_denom(s), GNC_HOW_RND_ROUND_HALF_UP);
    1198              : 
    1199            2 :     SET_GAINS_A_VDIRTY(s);
    1200            2 :     mark_split (s);
    1201            2 :     qof_instance_set_dirty(QOF_INSTANCE(s));
    1202            2 :     xaccTransCommitEdit(s->parent);
    1203            2 :     LEAVE ("");
    1204              : }
    1205              : 
    1206              : static void
    1207            0 : qofSplitSetSharePrice (Split *split, gnc_numeric price)
    1208              : {
    1209            0 :     g_return_if_fail(split);
    1210            0 :     split->value = gnc_numeric_mul(xaccSplitGetAmount(split),
    1211            0 :                                    price, get_currency_denom(split),
    1212              :                                    GNC_HOW_RND_ROUND_HALF_UP);
    1213              : }
    1214              : 
    1215              : void
    1216           64 : xaccSplitSetSharePrice (Split *s, gnc_numeric price)
    1217              : {
    1218           64 :     if (!s) return;
    1219              : 
    1220           64 :     if (gnc_numeric_zero_p (price))
    1221            0 :         return;
    1222              : 
    1223           64 :     ENTER (" ");
    1224           64 :     xaccTransBeginEdit (s->parent);
    1225              : 
    1226           64 :     s->value = gnc_numeric_mul(xaccSplitGetAmount(s),
    1227           64 :                                price, get_currency_denom(s),
    1228              :                                GNC_HOW_RND_ROUND_HALF_UP);
    1229              : 
    1230           64 :     SET_GAINS_VDIRTY(s);
    1231           64 :     mark_split (s);
    1232           64 :     qof_instance_set_dirty(QOF_INSTANCE(s));
    1233           64 :     xaccTransCommitEdit(s->parent);
    1234           64 :     LEAVE ("");
    1235              : }
    1236              : 
    1237              : static void
    1238            0 : qofSplitSetAmount (Split *split, gnc_numeric amt)
    1239              : {
    1240            0 :     g_return_if_fail(split);
    1241            0 :     if (split->acc)
    1242              :     {
    1243            0 :         split->amount = gnc_numeric_convert(amt,
    1244            0 :                                             get_commodity_denom(split), GNC_HOW_RND_ROUND_HALF_UP);
    1245              :     }
    1246              :     else
    1247              :     {
    1248            0 :         split->amount = amt;
    1249              :     }
    1250              : }
    1251              : 
    1252              : /* The amount of the split in the _account's_ commodity. */
    1253              : void
    1254        14505 : xaccSplitSetAmount (Split *s, gnc_numeric amt)
    1255              : {
    1256        14505 :     if (!s) return;
    1257        14505 :     g_return_if_fail(gnc_numeric_check(amt) == GNC_ERROR_OK);
    1258        14505 :     ENTER ("(split=%p) old amt=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT
    1259              :            " new amt=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, s,
    1260              :            s->amount.num, s->amount.denom, amt.num, amt.denom);
    1261              : 
    1262        14505 :     xaccTransBeginEdit (s->parent);
    1263        14505 :     if (s->acc)
    1264              :     {
    1265        12676 :         s->amount = gnc_numeric_convert(amt, get_commodity_denom(s),
    1266              :                                         GNC_HOW_RND_ROUND_HALF_UP);
    1267        12676 :         g_assert (gnc_numeric_check (s->amount) == GNC_ERROR_OK);
    1268              :     }
    1269              :     else
    1270         1829 :         s->amount = amt;
    1271        14505 :     s->adjusted_amount = s->amount;
    1272              : 
    1273        14505 :     SET_GAINS_ADIRTY(s);
    1274        14505 :     mark_split (s);
    1275        14505 :     qof_instance_set_dirty(QOF_INSTANCE(s));
    1276        14505 :     xaccTransCommitEdit(s->parent);
    1277        14505 :     LEAVE("");
    1278              : }
    1279              : 
    1280              : static void
    1281            0 : qofSplitSetValue (Split *split, gnc_numeric amt)
    1282              : {
    1283            0 :     g_return_if_fail(split);
    1284            0 :     split->value = gnc_numeric_convert(amt,
    1285            0 :                                        get_currency_denom(split), GNC_HOW_RND_ROUND_HALF_UP);
    1286            0 :     g_assert(gnc_numeric_check (split->value) != GNC_ERROR_OK);
    1287              : }
    1288              : 
    1289              : /* The value of the split in the _transaction's_ currency. */
    1290              : void
    1291        14823 : xaccSplitSetValue (Split *s, gnc_numeric amt)
    1292              : {
    1293              :     gnc_numeric new_val;
    1294        14823 :     if (!s) return;
    1295              : 
    1296        14823 :     g_return_if_fail(gnc_numeric_check(amt) == GNC_ERROR_OK);
    1297        14823 :     ENTER ("(split=%p) old val=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT
    1298              :            " new val=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, s,
    1299              :            s->value.num, s->value.denom, amt.num, amt.denom);
    1300              : 
    1301        14823 :     xaccTransBeginEdit (s->parent);
    1302        14823 :     new_val = gnc_numeric_convert(amt, get_currency_denom(s),
    1303              :                                   GNC_HOW_RND_ROUND_HALF_UP);
    1304        29646 :     if (gnc_numeric_check(new_val) == GNC_ERROR_OK &&
    1305        14823 :         !(gnc_numeric_zero_p (new_val) && !gnc_numeric_zero_p (amt)))
    1306              :     {
    1307        14817 :         s->value = new_val;
    1308              :     }
    1309              :     else
    1310              :     {
    1311            6 :         PERR("numeric error %s in converting the split value's denominator with amount %s and denom %d",
    1312              :             gnc_numeric_errorCode_to_string(gnc_numeric_check(new_val)),
    1313              :             gnc_num_dbg_to_string (amt), get_currency_denom(s));
    1314              :     }
    1315              : 
    1316        14823 :     SET_GAINS_VDIRTY(s);
    1317        14823 :     mark_split (s);
    1318        14823 :     qof_instance_set_dirty(QOF_INSTANCE(s));
    1319        14823 :     xaccTransCommitEdit(s->parent);
    1320        14823 :     LEAVE ("");
    1321              : }
    1322              : 
    1323              : /********************************************************************\
    1324              : \********************************************************************/
    1325              : 
    1326              : gnc_numeric
    1327          993 : xaccSplitGetBalance (const Split *s)
    1328              : {
    1329          993 :     return s ? s->balance : gnc_numeric_zero();
    1330              : }
    1331              : 
    1332              : gnc_numeric
    1333         3820 : xaccSplitGetNoclosingBalance (const Split *s)
    1334              : {
    1335         3820 :     return s ? s->noclosing_balance : gnc_numeric_zero();
    1336              : }
    1337              : 
    1338              : gnc_numeric
    1339            0 : xaccSplitGetClearedBalance (const Split *s)
    1340              : {
    1341            0 :     return s ? s->cleared_balance : gnc_numeric_zero();
    1342              : }
    1343              : 
    1344              : gnc_numeric
    1345            0 : xaccSplitGetReconciledBalance (const Split *s)
    1346              : {
    1347            0 :     return s ? s->reconciled_balance : gnc_numeric_zero();
    1348              : }
    1349              : 
    1350              : gnc_numeric
    1351         2245 : xaccSplitGetAdjustedAmount (const Split *s)
    1352              : {
    1353         2245 :     return s ? s->adjusted_amount : gnc_numeric_zero();
    1354              : }
    1355              : 
    1356              : void
    1357         1213 : xaccSplitSetAdjustedAmount (Split *s, gnc_numeric amt)
    1358              : {
    1359         1213 :     if  (!s) return;
    1360         1213 :     if (gnc_numeric_check (amt)) return;
    1361              : 
    1362         1213 :     s->adjusted_amount = amt;
    1363              : }
    1364              : 
    1365              : void
    1366          156 : xaccSplitSetBaseValue (Split *s, gnc_numeric value,
    1367              :                        const gnc_commodity * base_currency)
    1368              : {
    1369              :     const gnc_commodity *currency;
    1370              :     const gnc_commodity *commodity;
    1371              : 
    1372          156 :     if (!s) return;
    1373          156 :     xaccTransBeginEdit (s->parent);
    1374              : 
    1375          156 :     if (!s->acc)
    1376              :     {
    1377            1 :         PERR ("split must have a parent account");
    1378            1 :         return;
    1379              :     }
    1380              : 
    1381          155 :     currency = xaccTransGetCurrency (s->parent);
    1382          155 :     commodity = xaccAccountGetCommodity (s->acc);
    1383              : 
    1384              :     /* If the base_currency is the transaction's commodity ('currency'),
    1385              :      * set the value.  If it's the account commodity, set the
    1386              :      * amount. If both, set both. */
    1387          155 :     if (gnc_commodity_equiv(currency, base_currency))
    1388              :     {
    1389          153 :         if (gnc_commodity_equiv(commodity, base_currency))
    1390              :         {
    1391          152 :             s->amount = gnc_numeric_convert(value,
    1392          152 :                                             get_commodity_denom(s),
    1393              :                                             GNC_HOW_RND_ROUND_HALF_UP);
    1394              :         }
    1395          153 :         s->value = gnc_numeric_convert(value,
    1396          153 :                                        get_currency_denom(s),
    1397              :                                        GNC_HOW_RND_ROUND_HALF_UP);
    1398              :     }
    1399            2 :     else if (gnc_commodity_equiv(commodity, base_currency))
    1400              :     {
    1401            1 :         s->amount = gnc_numeric_convert(value, get_commodity_denom(s),
    1402              :                                         GNC_HOW_RND_ROUND_HALF_UP);
    1403              :     }
    1404              :     else
    1405              :     {
    1406            1 :         PERR ("inappropriate base currency %s "
    1407              :               "given split currency=%s and commodity=%s\n",
    1408              :               gnc_commodity_get_printname(base_currency),
    1409              :               gnc_commodity_get_printname(currency),
    1410              :               gnc_commodity_get_printname(commodity));
    1411            1 :         return;
    1412              :     }
    1413              : 
    1414          154 :     SET_GAINS_A_VDIRTY(s);
    1415          154 :     mark_split (s);
    1416          154 :     qof_instance_set_dirty(QOF_INSTANCE(s));
    1417          154 :     xaccTransCommitEdit(s->parent);
    1418              : }
    1419              : 
    1420              : gnc_numeric
    1421            0 : xaccSplitGetBaseValue (const Split *s, const gnc_commodity * base_currency)
    1422              : {
    1423            0 :     if (!s || !s->acc || !s->parent) return gnc_numeric_zero();
    1424              : 
    1425              :     /* be more precise -- the value depends on the currency we want it
    1426              :      * expressed in.  */
    1427            0 :     if (gnc_commodity_equiv(xaccTransGetCurrency(s->parent), base_currency))
    1428            0 :         return xaccSplitGetValue(s);
    1429            0 :     if (gnc_commodity_equiv(xaccAccountGetCommodity(s->acc), base_currency))
    1430            0 :         return xaccSplitGetAmount(s);
    1431              : 
    1432            0 :     PERR ("inappropriate base currency %s "
    1433              :           "given split currency=%s and commodity=%s\n",
    1434              :           gnc_commodity_get_printname(base_currency),
    1435              :           gnc_commodity_get_printname(xaccTransGetCurrency (s->parent)),
    1436              :           gnc_commodity_get_printname(xaccAccountGetCommodity(s->acc)));
    1437            0 :     return gnc_numeric_zero();
    1438              : }
    1439              : 
    1440              : /********************************************************************\
    1441              : \********************************************************************/
    1442              : 
    1443              : gnc_numeric
    1444          154 : xaccSplitConvertAmount (const Split *split, const Account * account)
    1445              : {
    1446              :     gnc_commodity *acc_com, *to_commodity;
    1447              :     Transaction *txn;
    1448              :     gnc_numeric amount, value, convrate;
    1449              :     Account * split_acc;
    1450              : 
    1451          154 :     amount = xaccSplitGetAmount (split);
    1452              : 
    1453              :     /* If this split is attached to this account, OR */
    1454          154 :     split_acc = xaccSplitGetAccount (split);
    1455          154 :     if (split_acc == account)
    1456          105 :         return amount;
    1457              : 
    1458              :     /* If split->account->commodity == to_commodity, return the amount */
    1459           49 :     acc_com = xaccAccountGetCommodity (split_acc);
    1460           49 :     to_commodity = xaccAccountGetCommodity (account);
    1461           49 :     if (acc_com && gnc_commodity_equal (acc_com, to_commodity))
    1462           45 :         return amount;
    1463              : 
    1464              :     /* Ok, this split is not for the viewed account, and the commodity
    1465              :      * does not match.  So we need to do some conversion.
    1466              :      *
    1467              :      * First, we can cheat.  If this transaction is balanced and has
    1468              :      * exactly two splits, then we can implicitly determine the exchange
    1469              :      * rate and just return the 'other' split amount.
    1470              :      */
    1471            4 :     txn = xaccSplitGetParent (split);
    1472            4 :     if (txn && xaccTransIsBalanced (txn))
    1473              :     {
    1474            2 :         const Split *osplit = xaccSplitGetOtherSplit (split);
    1475              : 
    1476            2 :         if (osplit)
    1477              :         {
    1478              :             gnc_commodity* split_comm =
    1479            2 :                 xaccAccountGetCommodity(xaccSplitGetAccount(osplit));
    1480            2 :             if (!gnc_commodity_equal(to_commodity, split_comm))
    1481              :             {
    1482              :                 gchar guidstr[GUID_ENCODING_LENGTH+1];
    1483            1 :                 guid_to_string_buff(xaccSplitGetGUID(osplit),guidstr);
    1484            1 :                 PERR("The split's (%s) amount can't be converted from %s into %s.",
    1485              :                      guidstr,
    1486              :                      gnc_commodity_get_mnemonic(split_comm),
    1487              :                      gnc_commodity_get_mnemonic(to_commodity)
    1488              :                     );
    1489            1 :                 return gnc_numeric_zero();
    1490              :             }
    1491            1 :             return gnc_numeric_neg (xaccSplitGetAmount (osplit));
    1492              :         }
    1493              :     }
    1494              : 
    1495              :     /* ... otherwise, we need to compute the amount from the conversion
    1496              :      * rate into _this account_.  So, find the split into this account,
    1497              :      * compute the conversion rate (based on amount/value), and then multiply
    1498              :      * this times the split value.
    1499              :      */
    1500            2 :     value = xaccSplitGetValue (split);
    1501              : 
    1502            2 :     if (gnc_numeric_zero_p (value))
    1503              :     {
    1504            1 :         return value;
    1505              :     }
    1506              : 
    1507            1 :     convrate = xaccTransGetAccountConvRate(txn, account);
    1508            1 :     return gnc_numeric_mul (value, convrate,
    1509            1 :                             gnc_commodity_get_fraction (to_commodity),
    1510            1 :                             GNC_HOW_RND_ROUND_HALF_UP);
    1511              : }
    1512              : 
    1513              : /********************************************************************\
    1514              : \********************************************************************/
    1515              : 
    1516              : gboolean
    1517         4958 : xaccSplitDestroy (Split *split)
    1518              : {
    1519              :     Account *acc;
    1520              :     Transaction *trans;
    1521              :     GncEventData ed;
    1522              : 
    1523         4958 :     if (!split) return TRUE;
    1524              : 
    1525         4958 :     acc = split->acc;
    1526         4958 :     trans = split->parent;
    1527         4958 :     if (acc && !qof_instance_get_destroying(acc)
    1528         4939 :         && !qof_instance_get_destroying(trans)
    1529         9916 :         && xaccTransGetReadOnly(trans))
    1530            0 :         return FALSE;
    1531              : 
    1532         4958 :     xaccTransBeginEdit(trans);
    1533         4958 :     ed.node = split;
    1534         4958 :     ed.idx = xaccTransGetSplitIndex(trans, split);
    1535         4958 :     qof_instance_set_dirty(QOF_INSTANCE(split));
    1536         4958 :     qof_instance_set_destroying(split, TRUE);
    1537         4958 :     qof_event_gen(&trans->inst, GNC_EVENT_ITEM_REMOVED, &ed);
    1538         4958 :     xaccTransCommitEdit(trans);
    1539              : 
    1540         4958 :     return TRUE;
    1541              : }
    1542              : 
    1543              : /********************************************************************\
    1544              : \********************************************************************/
    1545              : 
    1546              : gint
    1547       668431 : xaccSplitOrder (const Split *sa, const Split *sb)
    1548              : {
    1549              :     int retval;
    1550              :     int comp;
    1551              :     const char *da, *db;
    1552              :     gboolean action_for_num;
    1553              : 
    1554       668431 :     if (sa == sb) return 0;
    1555              :     /* nothing is always less than something */
    1556       668430 :     if (!sa) return -1;
    1557       668429 :     if (!sb) return +1;
    1558              : 
    1559              :     /* sort in transaction order, but use split action rather than trans num
    1560              :      * according to book option */
    1561              :     action_for_num = qof_book_use_split_action_for_num_field
    1562       668428 :         (xaccSplitGetBook (sa));
    1563       668428 :     if (action_for_num)
    1564            2 :         retval = xaccTransOrder_num_action (sa->parent, sa->action,
    1565            2 :                                             sb->parent, sb->action);
    1566              :     else
    1567       668426 :         retval = xaccTransOrder (sa->parent, sb->parent);
    1568       668428 :     if (retval) return retval;
    1569              : 
    1570              :     /* otherwise, sort on memo strings */
    1571         9076 :     da = sa->memo ? sa->memo : "";
    1572         9076 :     db = sb->memo ? sb->memo : "";
    1573         9076 :     retval = g_utf8_collate (da, db);
    1574         9076 :     if (retval)
    1575          192 :         return retval;
    1576              : 
    1577              :     /* otherwise, sort on action strings */
    1578         8884 :     da = sa->action ? sa->action : "";
    1579         8884 :     db = sb->action ? sb->action : "";
    1580         8884 :     retval = g_utf8_collate (da, db);
    1581         8884 :     if (retval != 0)
    1582          570 :         return retval;
    1583              : 
    1584              :     /* the reconciled flag ... */
    1585         8314 :     if (sa->reconciled < sb->reconciled) return -1;
    1586         8313 :     if (sa->reconciled > sb->reconciled) return +1;
    1587              : 
    1588              :     /* compare amounts */
    1589         8312 :     comp = gnc_numeric_compare(xaccSplitGetAmount(sa), xaccSplitGetAmount (sb));
    1590         8312 :     if (comp < 0) return -1;
    1591         4934 :     if (comp > 0) return +1;
    1592              : 
    1593          277 :     comp = gnc_numeric_compare(xaccSplitGetValue(sa), xaccSplitGetValue (sb));
    1594          277 :     if (comp < 0) return -1;
    1595          274 :     if (comp > 0) return +1;
    1596              : 
    1597              :     /* if dates differ, return */
    1598          271 :     if (sa->date_reconciled < sb->date_reconciled)
    1599            1 :         return -1;
    1600          270 :     else if (sa->date_reconciled > sb->date_reconciled)
    1601            2 :         return 1;
    1602              : 
    1603              :     /* else, sort on guid - keeps sort stable. */
    1604          268 :     retval = qof_instance_guid_compare(sa, sb);
    1605          268 :     if (retval) return retval;
    1606              : 
    1607            0 :     return 0;
    1608              : }
    1609              : 
    1610              : gint
    1611          218 : xaccSplitOrderDateOnly (const Split *sa, const Split *sb)
    1612              : {
    1613              :     Transaction *ta, *tb;
    1614              : 
    1615          218 :     if (sa == sb) return 0;
    1616              :     /* nothing is always less than something */
    1617          217 :     if (!sa) return -1;
    1618          216 :     if (!sb) return +1;
    1619              : 
    1620          215 :     ta = sa->parent;
    1621          215 :     tb = sb->parent;
    1622          215 :     if ( !ta && !tb ) return 0;
    1623          214 :     if ( !tb ) return -1;
    1624          213 :     if ( !ta ) return +1;
    1625              : 
    1626          212 :     if (ta->date_posted == tb->date_posted)
    1627          110 :         return -1; // Keep the same order
    1628          102 :     return (ta->date_posted > tb->date_posted) - (ta->date_posted < tb->date_posted);
    1629              : }
    1630              : 
    1631              : static gboolean
    1632           29 : get_corr_account_split(const Split *sa, const Split **retval)
    1633              : {
    1634           29 :     *retval = nullptr;
    1635           29 :     g_return_val_if_fail(sa, FALSE);
    1636              : 
    1637           28 :     if (xaccTransCountSplits (sa->parent) > 2)
    1638            2 :         return FALSE;
    1639              : 
    1640           26 :     *retval = xaccSplitGetOtherSplit (sa);
    1641           26 :     if (*retval)
    1642           23 :         return TRUE;
    1643              :     else
    1644            3 :         return FALSE;
    1645              : }
    1646              : 
    1647              : /* TODO: these static consts can be shared. */
    1648              : const char *
    1649            0 : xaccSplitGetCorrAccountName(const Split *sa)
    1650              : {
    1651              :     static const char *split_const = nullptr;
    1652              :     const Split *other_split;
    1653              : 
    1654            0 :     if (!get_corr_account_split(sa, &other_split))
    1655              :     {
    1656            0 :         if (!split_const)
    1657            0 :             split_const = _("-- Split Transaction --");
    1658              : 
    1659            0 :         return split_const;
    1660              :     }
    1661              : 
    1662            0 :     return xaccAccountGetName(other_split->acc);
    1663              : }
    1664              : 
    1665              : char *
    1666           20 : xaccSplitGetCorrAccountFullName(const Split *sa)
    1667              : {
    1668              :     static const char *split_const = nullptr;
    1669              :     const Split *other_split;
    1670              : 
    1671           20 :     if (!get_corr_account_split(sa, &other_split))
    1672              :     {
    1673            1 :         if (!split_const)
    1674            1 :             split_const = _("-- Split Transaction --");
    1675              : 
    1676            2 :         return g_strdup(split_const);
    1677              :     }
    1678           19 :     return gnc_account_get_full_name(other_split->acc);
    1679              : }
    1680              : 
    1681              : const char *
    1682            4 : xaccSplitGetCorrAccountCode(const Split *sa)
    1683              : {
    1684              :     static const char *split_const = nullptr;
    1685              :     const Split *other_split;
    1686              : 
    1687            4 :     if (!get_corr_account_split(sa, &other_split))
    1688              :     {
    1689            1 :         if (!split_const)
    1690            1 :             split_const = C_("Displayed account code of the other account in a multi-split transaction", "Split");
    1691              : 
    1692            1 :         return split_const;
    1693              :     }
    1694            3 :     return xaccAccountGetCode(other_split->acc);
    1695              : }
    1696              : 
    1697              : /* TODO: It's not too hard to make this function avoid the malloc/free. */
    1698              : int
    1699         1552 : xaccSplitCompareAccountFullNames(const Split *sa, const Split *sb)
    1700              : {
    1701              :     Account *aa, *ab;
    1702         1552 :     if (sa == sb) return 0;
    1703         1551 :     if (!sa) return -1;
    1704         1550 :     if (!sb) return 1;
    1705              : 
    1706         1549 :     aa = sa->acc;
    1707         1549 :     ab = sb->acc;
    1708         1549 :     if (aa == ab) return 0;
    1709              : 
    1710          109 :     auto path_a = gnc_account_get_all_parents (aa);
    1711          109 :     auto path_b = gnc_account_get_all_parents (ab);
    1712          109 :     auto mismatch_pair = std::mismatch (path_a.rbegin(), path_a.rend(),
    1713          218 :                                         path_b.rbegin(), path_b.rend());
    1714              : 
    1715          109 :     return mismatch_pair.first == path_a.rend() ? -1
    1716          109 :         : mismatch_pair.second == path_b.rend() ? 1
    1717          109 :         : g_utf8_collate (xaccAccountGetName (*mismatch_pair.first),
    1718            0 :                           xaccAccountGetName (*mismatch_pair.second));
    1719            0 : }
    1720              : 
    1721              : 
    1722              : int
    1723            4 : xaccSplitCompareAccountCodes(const Split *sa, const Split *sb)
    1724              : {
    1725              :     Account *aa, *ab;
    1726            4 :     if (!sa && !sb) return 0;
    1727            3 :     if (!sa) return -1;
    1728            2 :     if (!sb) return 1;
    1729              : 
    1730            1 :     aa = sa->acc;
    1731            1 :     ab = sb->acc;
    1732              : 
    1733            1 :     return g_strcmp0(xaccAccountGetCode(aa), xaccAccountGetCode(ab));
    1734              : }
    1735              : 
    1736              : int
    1737           12 : xaccSplitCompareOtherAccountFullNames(const Split *sa, const Split *sb)
    1738              : {
    1739              :     char *ca, *cb;
    1740              :     int retval;
    1741           12 :     if (!sa && !sb) return 0;
    1742           11 :     if (!sa) return -1;
    1743           10 :     if (!sb) return 1;
    1744              : 
    1745              :     /* doesn't matter what separator we use
    1746              :      * as long as they are the same
    1747              :      */
    1748              : 
    1749            9 :     ca = xaccSplitGetCorrAccountFullName(sa);
    1750            9 :     cb = xaccSplitGetCorrAccountFullName(sb);
    1751            9 :     retval = g_strcmp0(ca, cb);
    1752            9 :     g_free(ca);
    1753            9 :     g_free(cb);
    1754            9 :     return retval;
    1755              : }
    1756              : 
    1757              : int
    1758            4 : xaccSplitCompareOtherAccountCodes(const Split *sa, const Split *sb)
    1759              : {
    1760              :     const char *ca, *cb;
    1761            4 :     if (!sa && !sb) return 0;
    1762            3 :     if (!sa) return -1;
    1763            2 :     if (!sb) return 1;
    1764              : 
    1765            1 :     ca = xaccSplitGetCorrAccountCode(sa);
    1766            1 :     cb = xaccSplitGetCorrAccountCode(sb);
    1767            1 :     return g_strcmp0(ca, cb);
    1768              : }
    1769              : 
    1770              : static void
    1771            0 : qofSplitSetMemo (Split *split, const char* memo)
    1772              : {
    1773            0 :     g_return_if_fail(split);
    1774            0 :     CACHE_REPLACE(split->memo, memo);
    1775              : }
    1776              : 
    1777              : void
    1778         1883 : xaccSplitSetMemo (Split *split, const char *memo)
    1779              : {
    1780         1883 :     if (!split || !memo) return;
    1781         1782 :     xaccTransBeginEdit (split->parent);
    1782              : 
    1783         1782 :     CACHE_REPLACE(split->memo, memo);
    1784         1782 :     qof_instance_set_dirty(QOF_INSTANCE(split));
    1785         1782 :     xaccTransCommitEdit(split->parent);
    1786              : 
    1787              : }
    1788              : 
    1789              : static void
    1790            0 : qofSplitSetAction (Split *split, const char *actn)
    1791              : {
    1792            0 :     g_return_if_fail(split);
    1793            0 :     CACHE_REPLACE(split->action, actn);
    1794              : }
    1795              : 
    1796              : void
    1797         1110 : xaccSplitSetAction (Split *split, const char *actn)
    1798              : {
    1799         1110 :     if (!split || !actn) return;
    1800         1110 :     xaccTransBeginEdit (split->parent);
    1801              : 
    1802         1110 :     CACHE_REPLACE(split->action, actn);
    1803         1110 :     qof_instance_set_dirty(QOF_INSTANCE(split));
    1804         1110 :     xaccTransCommitEdit(split->parent);
    1805              : 
    1806              : }
    1807              : 
    1808              : static void
    1809            0 : qofSplitSetReconcile (Split *split, char recn)
    1810              : {
    1811            0 :     g_return_if_fail(split);
    1812            0 :     switch (recn)
    1813              :     {
    1814            0 :         case NREC:
    1815              :         case CREC:
    1816              :         case YREC:
    1817              :         case FREC:
    1818              :         case VREC:
    1819            0 :             split->reconciled = recn;
    1820            0 :             mark_split (split);
    1821            0 :             xaccAccountRecomputeBalance (split->acc);
    1822            0 :             break;
    1823            0 :         default:
    1824            0 :             PERR("Bad reconciled flag");
    1825            0 :             break;
    1826              :     }
    1827              : }
    1828              : 
    1829              : void
    1830         2932 : xaccSplitSetReconcile (Split *split, char recn)
    1831              : {
    1832         2932 :     if (!split || split->reconciled == recn) return;
    1833         1143 :     xaccTransBeginEdit (split->parent);
    1834              : 
    1835         1143 :     switch (recn)
    1836              :     {
    1837         1143 :         case NREC:
    1838              :         case CREC:
    1839              :         case YREC:
    1840              :         case FREC:
    1841              :         case VREC:
    1842         1143 :             split->reconciled = recn;
    1843         1143 :             mark_split (split);
    1844         1143 :             qof_instance_set_dirty(QOF_INSTANCE(split));
    1845         1143 :             xaccAccountRecomputeBalance (split->acc);
    1846         1143 :             break;
    1847            0 :         default:
    1848            0 :             PERR("Bad reconciled flag");
    1849            0 :             break;
    1850              :     }
    1851         1143 :     xaccTransCommitEdit(split->parent);
    1852              : 
    1853              : }
    1854              : 
    1855              : void
    1856          788 : xaccSplitSetDateReconciledSecs (Split *split, time64 secs)
    1857              : {
    1858          788 :     if (!split) return;
    1859          788 :     xaccTransBeginEdit (split->parent);
    1860              : 
    1861          788 :     split->date_reconciled = secs;
    1862          788 :     qof_instance_set_dirty(QOF_INSTANCE(split));
    1863          788 :     xaccTransCommitEdit(split->parent);
    1864              : 
    1865              : }
    1866              : 
    1867              : 
    1868              : /*################## Added for Reg2 #################*/
    1869              : time64
    1870          161 : xaccSplitGetDateReconciled (const Split * split)
    1871              : {
    1872          161 :     return split ? split->date_reconciled : 0;
    1873              : }
    1874              : /*################## Added for Reg2 #################*/
    1875              : 
    1876              : /********************************************************************\
    1877              : \********************************************************************/
    1878              : 
    1879              : /* return the parent transaction of the split */
    1880              : Transaction *
    1881       113804 : xaccSplitGetParent (const Split *split)
    1882              : {
    1883       113804 :     return split ? split->parent : nullptr;
    1884              : }
    1885              : 
    1886              : void
    1887         7174 : xaccSplitSetParent(Split *s, Transaction *t)
    1888              : {
    1889              :     Transaction *old_trans;
    1890              :     GncEventData ed;
    1891              : 
    1892         7176 :     g_return_if_fail(s);
    1893         7174 :     if (s->parent == t) return;
    1894              : 
    1895         7172 :     if (s->parent != s->orig_parent && s->orig_parent != t)
    1896            1 :         PERR("You may not add the split to more than one transaction"
    1897              :              " during the BeginEdit/CommitEdit block.");
    1898         7172 :     xaccTransBeginEdit(t);
    1899         7172 :     old_trans = s->parent;
    1900              : 
    1901         7172 :     xaccTransBeginEdit(old_trans);
    1902              : 
    1903         7172 :     ed.node = s;
    1904         7172 :     if (old_trans)
    1905              :     {
    1906           12 :         ed.idx = xaccTransGetSplitIndex(old_trans, s);
    1907           12 :         qof_event_gen(&old_trans->inst, GNC_EVENT_ITEM_REMOVED, &ed);
    1908              :     }
    1909         7172 :     s->parent = t;
    1910         7172 :     qof_instance_set_dirty(QOF_INSTANCE(s));
    1911              : 
    1912         7172 :     xaccTransCommitEdit(old_trans);
    1913              : 
    1914         7172 :     if (t)
    1915              :     {
    1916              :         /* Convert split to new transaction's commodity denominator */
    1917         7164 :         xaccSplitSetValue(s, xaccSplitGetValue(s));
    1918              : 
    1919              :         /* add ourselves to the new transaction's list of pending splits. */
    1920         7164 :         if (nullptr == g_list_find(t->splits, s))
    1921         7164 :             t->splits = g_list_append(t->splits, s);
    1922              : 
    1923         7164 :         ed.idx = -1; /* unused */
    1924         7164 :         qof_event_gen(&t->inst, GNC_EVENT_ITEM_ADDED, &ed);
    1925              :     }
    1926         7172 :     xaccTransCommitEdit(t);
    1927              : }
    1928              : 
    1929              : 
    1930              : GNCLot *
    1931          518 : xaccSplitGetLot (const Split *split)
    1932              : {
    1933          518 :     return split ? split->lot : nullptr;
    1934              : }
    1935              : 
    1936              : void
    1937          294 : xaccSplitSetLot(Split* split, GNCLot* lot)
    1938              : {
    1939          294 :     xaccTransBeginEdit (split->parent);
    1940          294 :     split->lot = lot;
    1941          294 :     qof_instance_set_dirty(QOF_INSTANCE(split));
    1942          294 :     xaccTransCommitEdit(split->parent);
    1943          294 : }
    1944              : 
    1945              : const char *
    1946         2463 : xaccSplitGetMemo (const Split *split)
    1947              : {
    1948         2463 :     return split ? split->memo : nullptr;
    1949              : }
    1950              : 
    1951              : void
    1952            6 : xaccSplitSetOnlineID (Split *split, const char *id)
    1953              : {
    1954            6 :     if (!split) return;
    1955              :     /* No xaccTransBeginEdit/CommitEdit here: the online_id is normally set
    1956              :      * while many other parameters of a transaction are being changed, so
    1957              :      * the caller wraps this in the parent transaction's edit. */
    1958            6 :     std::optional<const char*> val;
    1959            6 :     if (id && *id)
    1960            3 :         val = g_strdup (id);
    1961           18 :     qof_instance_set_path_kvp<const char*> (QOF_INSTANCE(split), val, {split_online_id});
    1962            6 :     qof_instance_set_dirty (QOF_INSTANCE(split));
    1963           12 : }
    1964              : 
    1965              : const char *
    1966           13 : xaccSplitGetOnlineID (const Split *split)
    1967              : {
    1968           13 :     if (!split) return nullptr;
    1969           39 :     auto rv{qof_instance_get_path_kvp<const char*> (QOF_INSTANCE(split), {split_online_id})};
    1970           13 :     return rv ? *rv : nullptr;
    1971           26 : }
    1972              : 
    1973              : gboolean
    1974            6 : xaccSplitHasOnlineID (const Split *split)
    1975              : {
    1976            6 :     auto id = xaccSplitGetOnlineID (split);
    1977            6 :     return (id && *id);
    1978              : }
    1979              : 
    1980              : const char *
    1981         1429 : xaccSplitGetAction (const Split *split)
    1982              : {
    1983         1429 :     return split ? split->action : nullptr;
    1984              : }
    1985              : 
    1986              : char
    1987        32910 : xaccSplitGetReconcile (const Split *split)
    1988              : {
    1989        32910 :     return split ? split->reconciled : ' ';
    1990              : }
    1991              : 
    1992              : 
    1993              : gnc_numeric
    1994       158969 : xaccSplitGetAmount (const Split * split)
    1995              : {
    1996       158969 :     return split ? split->amount : gnc_numeric_zero();
    1997              : }
    1998              : 
    1999              : gnc_numeric
    2000        55903 : xaccSplitGetValue (const Split * split)
    2001              : {
    2002        55903 :     return split ? split->value : gnc_numeric_zero();
    2003              : }
    2004              : 
    2005              : gnc_numeric
    2006          416 : xaccSplitGetSharePrice (const Split * split)
    2007              : {
    2008              :     gnc_numeric amt, val, price;
    2009          416 :     if (!split) return gnc_numeric_create(0, 1);
    2010              : 
    2011              : 
    2012              :     /* if amount == 0, return 0
    2013              :      * otherwise return value/amount
    2014              :      */
    2015              : 
    2016          415 :     amt = xaccSplitGetAmount(split);
    2017          415 :     val = xaccSplitGetValue(split);
    2018          415 :     if (gnc_numeric_zero_p(amt))
    2019           48 :         return gnc_numeric_create(0, 1);
    2020              : 
    2021          367 :     price = gnc_numeric_div(val, amt,
    2022              :                             GNC_DENOM_AUTO,
    2023              :                             GNC_HOW_RND_ROUND_HALF_UP);
    2024              : 
    2025              :     /* During random checks we can get some very weird prices.  Let's
    2026              :      * handle some overflow and other error conditions by returning
    2027              :      * zero.  But still print an error to let us know it happened.
    2028              :      */
    2029          367 :     if (gnc_numeric_check(price))
    2030              :     {
    2031            2 :         PERR("Computing share price failed (%d): [ %" G_GINT64_FORMAT " / %"
    2032              :              G_GINT64_FORMAT " ] / [ %" G_GINT64_FORMAT " / %" G_GINT64_FORMAT " ]",
    2033              :              gnc_numeric_check(price), val.num, val.denom, amt.num, amt.denom);
    2034            2 :         return gnc_numeric_create(0, 1);
    2035              :     }
    2036              : 
    2037          365 :     return price;
    2038              : }
    2039              : 
    2040              : /********************************************************************\
    2041              : \********************************************************************/
    2042              : 
    2043              : QofBook *
    2044       686014 : xaccSplitGetBook (const Split *split)
    2045              : {
    2046       686014 :     return qof_instance_get_book(QOF_INSTANCE(split));
    2047              : }
    2048              : 
    2049              : const char *
    2050        97958 : xaccSplitGetType(Split *s)
    2051              : {
    2052        97958 :     if (!s) return nullptr;
    2053              : 
    2054        97958 :     if (!s->split_type)
    2055              :     {
    2056        14088 :         auto type{qof_instance_get_path_kvp<const char*> (QOF_INSTANCE(s), {"split-type"})};
    2057              : 
    2058         7044 :         if (!type || !g_strcmp0 (*type, split_type_normal))
    2059         7044 :             s->split_type = split_type_normal;
    2060            0 :         else if (!g_strcmp0 (*type, split_type_stock_split))
    2061            0 :             s->split_type = split_type_stock_split;
    2062              :         else
    2063              :         {
    2064            0 :             PERR ("unexpected split-type %s, reset to normal.", *type);
    2065            0 :             s->split_type = split_type_normal;
    2066              :         }
    2067              :     }
    2068        97958 :     return s->split_type;
    2069              : }
    2070              : 
    2071              : /* reconfigure a split to be a stock split - after this, you shouldn't
    2072              :    mess with the value, just the amount. */
    2073              : void
    2074           15 : xaccSplitMakeStockSplit(Split *s)
    2075              : {
    2076           15 :     xaccTransBeginEdit (s->parent);
    2077              : 
    2078           15 :     s->value = gnc_numeric_zero();
    2079           15 :     s->split_type = split_type_stock_split;
    2080           45 :     qof_instance_set_path_kvp<const char*> (QOF_INSTANCE(s), g_strdup(split_type_stock_split),
    2081              :                                             {"split-type"});
    2082           15 :     SET_GAINS_VDIRTY(s);
    2083           15 :     mark_split(s);
    2084           15 :     qof_instance_set_dirty(QOF_INSTANCE(s));
    2085           15 :     xaccTransCommitEdit(s->parent);
    2086           15 : }
    2087              : 
    2088              : gboolean
    2089        97941 : xaccSplitIsStockSplit (Split *s)
    2090              : {
    2091        97941 :     return g_strcmp0(split_type_stock_split, xaccSplitGetType(s)) == 0;
    2092              : }
    2093              : 
    2094              : void
    2095            8 : xaccSplitAddPeerSplit (Split *split, const Split *other_split,
    2096              :                        time64 timestamp)
    2097              : {
    2098              :     const GncGUID* guid;
    2099              : 
    2100            8 :     g_return_if_fail (split != nullptr);
    2101            8 :     g_return_if_fail (other_split != nullptr);
    2102              : 
    2103            8 :     guid = qof_instance_get_guid (QOF_INSTANCE (other_split));
    2104            8 :     xaccTransBeginEdit (split->parent);
    2105            8 :     qof_instance_kvp_add_guid (QOF_INSTANCE (split), "lot-split",
    2106            8 :                                gnc_time(nullptr), "peer_guid", guid_copy(guid));
    2107            8 :     mark_split (split);
    2108            8 :     qof_instance_set_dirty (QOF_INSTANCE (split));
    2109            8 :     xaccTransCommitEdit (split->parent);
    2110              : }
    2111              : 
    2112              : gboolean
    2113          114 : xaccSplitHasPeers (const Split *split)
    2114              : {
    2115          114 :     return qof_instance_has_slot (QOF_INSTANCE (split), "lot-split");
    2116              : }
    2117              : 
    2118              : gboolean
    2119            1 : xaccSplitIsPeerSplit (const Split *split, const Split *other_split)
    2120              : {
    2121              :     const GncGUID* guid;
    2122              : 
    2123            1 :     g_return_val_if_fail (split != nullptr, FALSE);
    2124            1 :     g_return_val_if_fail (other_split != nullptr, FALSE);
    2125              : 
    2126            1 :     guid = qof_instance_get_guid (QOF_INSTANCE (other_split));
    2127            1 :     return qof_instance_kvp_has_guid (QOF_INSTANCE (split), "lot-split",
    2128            1 :                                       "peer_guid", guid);
    2129              : }
    2130              : 
    2131              : void
    2132            0 : xaccSplitRemovePeerSplit (Split *split, const Split *other_split)
    2133              : {
    2134              :     const GncGUID* guid;
    2135              : 
    2136            0 :     g_return_if_fail (split != nullptr);
    2137            0 :     g_return_if_fail (other_split != nullptr);
    2138              : 
    2139            0 :     guid = qof_instance_get_guid (QOF_INSTANCE (other_split));
    2140            0 :     xaccTransBeginEdit (split->parent);
    2141            0 :     qof_instance_kvp_remove_guid (QOF_INSTANCE (split), "lot-split",
    2142              :                                   "peer_guid", guid);
    2143            0 :     mark_split (split);
    2144            0 :     qof_instance_set_dirty (QOF_INSTANCE (split));
    2145            0 :     xaccTransCommitEdit (split->parent);
    2146              : }
    2147              : 
    2148              : void
    2149            0 : xaccSplitMergePeerSplits (Split *split, const Split *other_split)
    2150              : {
    2151            0 :     xaccTransBeginEdit (split->parent);
    2152            0 :     qof_instance_kvp_merge_guids (QOF_INSTANCE (split),
    2153            0 :                                   QOF_INSTANCE (other_split), "lot-split");
    2154            0 :     mark_split (split);
    2155            0 :     qof_instance_set_dirty (QOF_INSTANCE (split));
    2156            0 :     xaccTransCommitEdit (split->parent);
    2157            0 : }
    2158              : 
    2159              : /********************************************************************\
    2160              : \********************************************************************/
    2161              : /* In the old world, the 'other split' was the other split of a
    2162              :  * transaction that contained only two splits.  In the new world,
    2163              :  * a split may have been cut up between multiple lots, although
    2164              :  * in a conceptual sense, if lots hadn't been used, there would be
    2165              :  * only a pair.  So we handle this conceptual case: we can still
    2166              :  * identify, unambiguously, the 'other' split when 'this' split
    2167              :  * as been cut up across lots.  We do this by looking for the
    2168              :  * 'lot-split' keyword, which occurs only in cut-up splits.
    2169              :  */
    2170              : 
    2171              : Split *
    2172          222 : xaccSplitGetOtherSplit (const Split *split)
    2173              : {
    2174              :     Transaction *trans;
    2175          222 :     Split *other = nullptr;
    2176              : 
    2177          222 :     if (!split) return nullptr;
    2178          221 :     trans = split->parent;
    2179          221 :     if (!trans) return nullptr;
    2180              : 
    2181          643 :     for (GList *n = xaccTransGetSplitList (trans); n; n = n->next)
    2182              :     {
    2183          462 :         Split *s = GNC_SPLIT(n->data);
    2184          926 :         if ((s == split) ||
    2185          257 :             (!xaccTransStillHasSplit(trans, s)) ||
    2186          975 :             (xaccAccountGetType (xaccSplitGetAccount (s)) == ACCT_TYPE_TRADING) ||
    2187          256 :             (qof_instance_has_slot (QOF_INSTANCE (s), "lot-split")))
    2188          207 :             continue;
    2189              : 
    2190          255 :         if (other)
    2191           39 :             return nullptr;
    2192              : 
    2193          216 :         other = s;
    2194              :     }
    2195          181 :     return other;
    2196              : }
    2197              : 
    2198              : /********************************************************************\
    2199              : \********************************************************************/
    2200              : 
    2201              : gnc_numeric
    2202           65 : xaccSplitVoidFormerAmount(const Split *split)
    2203              : {
    2204           65 :     g_return_val_if_fail(split, gnc_numeric_zero());
    2205          195 :     auto num{qof_instance_get_path_kvp<gnc_numeric> (QOF_INSTANCE(split), {void_former_amt_str})};
    2206           65 :     return num ? *num : gnc_numeric_zero();
    2207          130 : }
    2208              : 
    2209              : gnc_numeric
    2210           12 : xaccSplitVoidFormerValue(const Split *split)
    2211              : {
    2212           12 :     g_return_val_if_fail(split, gnc_numeric_zero());
    2213           36 :     auto num{qof_instance_get_path_kvp<gnc_numeric> (QOF_INSTANCE(split), {void_former_val_str})};
    2214           12 :     return num ? *num : gnc_numeric_zero();
    2215           24 : }
    2216              : 
    2217              : void
    2218          205 : xaccSplitVoid(Split *split)
    2219              : {
    2220          205 :     g_return_if_fail (GNC_IS_SPLIT(split));
    2221          615 :     qof_instance_set_path_kvp<gnc_numeric> (QOF_INSTANCE(split), xaccSplitGetAmount(split), {void_former_amt_str});
    2222          615 :     qof_instance_set_path_kvp<gnc_numeric> (QOF_INSTANCE(split), xaccSplitGetValue(split), {void_former_val_str});
    2223          205 :     qof_instance_set_dirty (QOF_INSTANCE(split));
    2224              : 
    2225          205 :     static gnc_numeric zero = gnc_numeric_zero();
    2226          205 :     xaccSplitSetAmount (split, zero);
    2227          205 :     xaccSplitSetValue (split, zero);
    2228          205 :     xaccSplitSetReconcile(split, VREC);
    2229          820 : }
    2230              : 
    2231              : void
    2232            7 : xaccSplitUnvoid(Split *split)
    2233              : {
    2234            7 :     g_return_if_fail (GNC_IS_SPLIT(split));
    2235            7 :     xaccSplitSetAmount (split, xaccSplitVoidFormerAmount(split));
    2236            7 :     xaccSplitSetValue (split, xaccSplitVoidFormerValue(split));
    2237            7 :     xaccSplitSetReconcile(split, NREC);
    2238           21 :     qof_instance_set_path_kvp<gnc_numeric> (QOF_INSTANCE(split), {}, {void_former_amt_str});
    2239           21 :     qof_instance_set_path_kvp<gnc_numeric> (QOF_INSTANCE(split), {}, {void_former_val_str});
    2240            7 :     qof_instance_set_dirty (QOF_INSTANCE(split));
    2241           28 : }
    2242              : 
    2243              : /********************************************************************\
    2244              : \********************************************************************/
    2245              : /* QofObject function implementation */
    2246              : 
    2247              : /* Hook into the QofObject registry */
    2248              : 
    2249              : #ifdef _MSC_VER
    2250              : /* MSVC compiler doesn't have C99 "designated initializers"
    2251              :  * so we wrap them in a macro that is empty on MSVC. */
    2252              : # define DI(x) /* */
    2253              : #else
    2254              : # define DI(x) x
    2255              : #endif
    2256              : static QofObject split_object_def =
    2257              : {
    2258              :     DI(.interface_version = ) QOF_OBJECT_VERSION,
    2259              :     DI(.e_type            = ) GNC_ID_SPLIT,
    2260              :     DI(.type_label        = ) "Split",
    2261              :     DI(.create            = ) (void* (*)(QofBook*))xaccMallocSplit,
    2262              :     DI(.book_begin        = ) nullptr,
    2263              :     DI(.book_end          = ) nullptr,
    2264              :     DI(.is_dirty          = ) qof_collection_is_dirty,
    2265              :     DI(.mark_clean        = ) qof_collection_mark_clean,
    2266              :     DI(.foreach           = ) qof_collection_foreach,
    2267              :     DI(.printable         = ) (const char * (*)(gpointer)) xaccSplitGetMemo,
    2268              :     DI(.version_cmp       = ) (int (*)(gpointer, gpointer)) qof_instance_version_cmp,
    2269              : };
    2270              : 
    2271              : static gpointer
    2272          613 : split_account_guid_getter (gpointer obj, const QofParam *p)
    2273              : {
    2274          613 :     Split *s = GNC_SPLIT(obj);
    2275              :     Account *acc;
    2276              : 
    2277          613 :     if (!s) return nullptr;
    2278          613 :     acc = xaccSplitGetAccount (s);
    2279          613 :     if (!acc) return nullptr;
    2280          613 :     return ((gpointer)xaccAccountGetGUID (acc));
    2281              : }
    2282              : 
    2283              : static double    /* internal use only */
    2284            0 : DxaccSplitGetShareAmount (const Split * split)
    2285              : {
    2286            0 :     return split ? gnc_numeric_to_double(xaccSplitGetAmount(split)) : 0.0;
    2287              : }
    2288              : 
    2289              : static gpointer
    2290         3112 : no_op (gpointer obj, const QofParam *p)
    2291              : {
    2292         3112 :     return obj;
    2293              : }
    2294              : 
    2295              : static void
    2296            0 : qofSplitSetParentTrans(Split *s, QofInstance *ent)
    2297              : {
    2298            0 :     Transaction *trans = (Transaction*)ent;
    2299              : 
    2300            0 :     g_return_if_fail(trans);
    2301            0 :     xaccSplitSetParent(s, trans);
    2302              : }
    2303              : 
    2304              : static void
    2305            0 : qofSplitSetAccount(Split *s, QofInstance *ent)
    2306              : {
    2307            0 :     Account *acc = (Account*)ent;
    2308              : 
    2309            0 :     g_return_if_fail(acc);
    2310            0 :     xaccSplitSetAccount(s, acc);
    2311              : }
    2312              : 
    2313           83 : gboolean xaccSplitRegister (void)
    2314              : {
    2315              :     static const QofParam params[] =
    2316              :         {
    2317              :             {
    2318              :                 SPLIT_DATE_RECONCILED, QOF_TYPE_DATE,
    2319              :                 (QofAccessFunc)xaccSplitGetDateReconciled,
    2320              :                 (QofSetterFunc)xaccSplitSetDateReconciledSecs
    2321              :             },
    2322              : 
    2323              :             /* d-* are deprecated query params, should not be used in new
    2324              :              * queries, should be removed from old queries. */
    2325              :             {
    2326              :                 "d-share-amount", QOF_TYPE_DOUBLE,
    2327              :                 (QofAccessFunc)DxaccSplitGetShareAmount, nullptr
    2328              :             },
    2329              :             {
    2330              :                 "d-share-int64", QOF_TYPE_INT64,
    2331              :                 (QofAccessFunc)qof_entity_get_guid, nullptr
    2332              :             },
    2333              :             {
    2334              :                 SPLIT_BALANCE, QOF_TYPE_NUMERIC,
    2335              :                 (QofAccessFunc)xaccSplitGetBalance, nullptr
    2336              :             },
    2337              :             {
    2338              :                 SPLIT_CLEARED_BALANCE, QOF_TYPE_NUMERIC,
    2339              :                 (QofAccessFunc)xaccSplitGetClearedBalance, nullptr
    2340              :             },
    2341              :             {
    2342              :                 SPLIT_RECONCILED_BALANCE, QOF_TYPE_NUMERIC,
    2343              :                 (QofAccessFunc)xaccSplitGetReconciledBalance, nullptr
    2344              :             },
    2345              :             {
    2346              :                 SPLIT_MEMO, QOF_TYPE_STRING,
    2347              :                 (QofAccessFunc)xaccSplitGetMemo, (QofSetterFunc)qofSplitSetMemo
    2348              :             },
    2349              :             {
    2350              :                 SPLIT_ACTION, QOF_TYPE_STRING,
    2351              :                 (QofAccessFunc)xaccSplitGetAction, (QofSetterFunc)qofSplitSetAction
    2352              :             },
    2353              :             {
    2354              :                 SPLIT_RECONCILE, QOF_TYPE_CHAR,
    2355              :                 (QofAccessFunc)xaccSplitGetReconcile,
    2356              :                 (QofSetterFunc)qofSplitSetReconcile
    2357              :             },
    2358              :             {
    2359              :                 SPLIT_AMOUNT, QOF_TYPE_NUMERIC,
    2360              :                 (QofAccessFunc)xaccSplitGetAmount, (QofSetterFunc)qofSplitSetAmount
    2361              :             },
    2362              :             {
    2363              :                 SPLIT_SHARE_PRICE, QOF_TYPE_NUMERIC,
    2364              :                 (QofAccessFunc)xaccSplitGetSharePrice,
    2365              :                 (QofSetterFunc)qofSplitSetSharePrice
    2366              :             },
    2367              :             {
    2368              :                 SPLIT_VALUE, QOF_TYPE_DEBCRED,
    2369              :                 (QofAccessFunc)xaccSplitGetValue, (QofSetterFunc)qofSplitSetValue
    2370              :             },
    2371              :             { SPLIT_TYPE, QOF_TYPE_STRING, (QofAccessFunc)xaccSplitGetType, nullptr },
    2372              :             {
    2373              :                 SPLIT_VOIDED_AMOUNT, QOF_TYPE_NUMERIC,
    2374              :                 (QofAccessFunc)xaccSplitVoidFormerAmount, nullptr
    2375              :             },
    2376              :             {
    2377              :                 SPLIT_VOIDED_VALUE, QOF_TYPE_NUMERIC,
    2378              :                 (QofAccessFunc)xaccSplitVoidFormerValue, nullptr
    2379              :             },
    2380              :             { SPLIT_LOT, GNC_ID_LOT, (QofAccessFunc)xaccSplitGetLot, nullptr },
    2381              :             {
    2382              :                 SPLIT_TRANS, GNC_ID_TRANS,
    2383              :                 (QofAccessFunc)xaccSplitGetParent,
    2384              :                 (QofSetterFunc)qofSplitSetParentTrans
    2385              :             },
    2386              :             {
    2387              :                 SPLIT_ACCOUNT, GNC_ID_ACCOUNT,
    2388              :                 (QofAccessFunc)xaccSplitGetAccount, (QofSetterFunc)qofSplitSetAccount
    2389              :             },
    2390              :             { SPLIT_ACCOUNT_GUID, QOF_TYPE_GUID, split_account_guid_getter, nullptr },
    2391              :             /*  these are no-ops to register the parameter names (for sorting) but
    2392              :                 they return an allocated object which getters cannot do.  */
    2393              :             { SPLIT_ACCT_FULLNAME, SPLIT_ACCT_FULLNAME, no_op, nullptr },
    2394              :             { SPLIT_CORR_ACCT_NAME, SPLIT_CORR_ACCT_NAME, no_op, nullptr },
    2395              :             { SPLIT_CORR_ACCT_CODE, SPLIT_CORR_ACCT_CODE, no_op, nullptr },
    2396              :             { QOF_PARAM_BOOK, QOF_ID_BOOK, (QofAccessFunc)xaccSplitGetBook, nullptr },
    2397              :             {
    2398              :                 QOF_PARAM_GUID, QOF_TYPE_GUID,
    2399              :                 (QofAccessFunc)qof_entity_get_guid, nullptr
    2400              :             },
    2401              :             { nullptr },
    2402              :         };
    2403              : 
    2404           83 :     qof_class_register (GNC_ID_SPLIT, (QofSortFunc)xaccSplitOrder, params);
    2405           83 :     qof_class_register (SPLIT_ACCT_FULLNAME,
    2406              :                         (QofSortFunc)xaccSplitCompareAccountFullNames, nullptr);
    2407           83 :     qof_class_register (SPLIT_CORR_ACCT_NAME,
    2408              :                         (QofSortFunc)xaccSplitCompareOtherAccountFullNames,
    2409              :                         nullptr);
    2410           83 :     qof_class_register (SPLIT_CORR_ACCT_CODE,
    2411              :                         (QofSortFunc)xaccSplitCompareOtherAccountCodes, nullptr);
    2412              : 
    2413           83 :     return qof_object_register (&split_object_def);
    2414              : }
    2415              : 
    2416              : SplitTestFunctions*
    2417           31 : _utest_split_fill_functions (void)
    2418              : {
    2419           31 :     SplitTestFunctions *func = g_new (SplitTestFunctions, 1);
    2420              : 
    2421           31 :     func->xaccSplitEqualCheckBal = xaccSplitEqualCheckBal;
    2422           31 :     func->get_currency_denom = get_currency_denom;
    2423           31 :     func->get_commodity_denom = get_commodity_denom;
    2424           31 :     func->get_corr_account_split = get_corr_account_split;
    2425           31 :     return func;
    2426              : }
    2427              : 
    2428              : /************************ END OF ************************************\
    2429              : \************************* FILE *************************************/
        

Generated by: LCOV version 2.0-1