LCOV - code coverage report
Current view: top level - libgnucash/engine - Transaction.cpp (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 82.0 % 1112 912
Test Date: 2025-07-28 11:54:01 Functions: 86.1 % 122 105
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : /********************************************************************\
       2                 :             :  * Transaction.c -- transaction 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                 :             : #include <stdint.h>
      40                 :             : #include <time.h>
      41                 :             : #ifdef HAVE_UNISTD_H
      42                 :             : # include <unistd.h>
      43                 :             : #endif
      44                 :             : 
      45                 :             : #include "AccountP.hpp"
      46                 :             : #include "Scrub.h"
      47                 :             : #include "Scrub3.h"
      48                 :             : #include "TransactionP.hpp"
      49                 :             : #include "SplitP.hpp"
      50                 :             : #include "TransLog.h"
      51                 :             : #include "cap-gains.h"
      52                 :             : #include "gnc-commodity.h"
      53                 :             : #include "gnc-engine.h"
      54                 :             : #include "gnc-lot.h"
      55                 :             : #include "gnc-event.h"
      56                 :             : #include <gnc-date.h>
      57                 :             : #include "SchedXaction.h"
      58                 :             : #include "gncBusiness.h"
      59                 :             : #include <qofinstance-p.h>
      60                 :             : #include "gncInvoice.h"
      61                 :             : #include "gncOwner.h"
      62                 :             : 
      63                 :             : /* Notes about xaccTransBeginEdit(), xaccTransCommitEdit(), and
      64                 :             :  *  xaccTransRollback():
      65                 :             :  *
      66                 :             :  * Why use it:
      67                 :             :  *
      68                 :             :  *   Data consistency: Wrapping your changes to financial data inside
      69                 :             :  *   a BeginEdit/CommitEdit block allows the engine to verify that
      70                 :             :  *   your changes still leave the financial objects in an internally
      71                 :             :  *   consistent state.  This is true even though you may make a series
      72                 :             :  *   of individual changes that are not consistent by themselves.  In
      73                 :             :  *   this way, it's like telling the engine, "Okay, I've finished my
      74                 :             :  *   edits.  Please check my work."
      75                 :             :  *
      76                 :             :  *   Data integrity: The other benefit of the BeginEdit/CommitEdit
      77                 :             :  *   block is that it allows the engine (and the backend) to remember
      78                 :             :  *   the last known correct state of your data.  This allows you to
      79                 :             :  *   undo any changes that you don't want to keep.  In this way, it's
      80                 :             :  *   like telling the engine telling the back end, "Yes, I really mean
      81                 :             :  *   it.  Remember this data." or "Nevermind, scratch that."  The
      82                 :             :  *   important feature here is that if things go bad, for whatever
      83                 :             :  *   reason (e.g. the application crashed, you lost the backend), your
      84                 :             :  *   data remains in the state it was in just after the previous
      85                 :             :  *   xaccTransCommitEdit().  [assuming no nesting, which probably
      86                 :             :  *   isn't useful outside the engine.]
      87                 :             :  *
      88                 :             :  *   Note that the backend doesn't care about data consistency -
      89                 :             :  *   that's the engine's job.
      90                 :             :  *
      91                 :             :  * Example Use:
      92                 :             :  *
      93                 :             :  *   xaccTransBeginEdit(trans);
      94                 :             :  *
      95                 :             :  *
      96                 :             :  *   split = xaccMallocSplit(book);
      97                 :             :  *   xaccSplitSetAccount(split, acc);
      98                 :             :  *   xaccSplitSetParent(split, trans);  // Adding a new split
      99                 :             :  *
     100                 :             :  *   xaccSplitSetValue(split, val);     // Changing a split
     101                 :             :  *
     102                 :             :  *   xaccSplitDestroy(split);           // Removing a split
     103                 :             :  *
     104                 :             :  *   xaccTransSetNum(trans, "501");     // Changing the trans
     105                 :             :  *
     106                 :             :  *   if (really_do_it)
     107                 :             :  *      xaccTransCommitEdit(trans);
     108                 :             :  *   else
     109                 :             :  *      xaccTransRollbackEdit(trans);
     110                 :             :  *
     111                 :             :  * How it works:
     112                 :             :  *
     113                 :             :  *   Calling xaccTransBeginEdit() starts a BeginEdit/CommitEdit block.
     114                 :             :  *   Inside the block any changes to the transaction or any splits in
     115                 :             :  *   the transaction are considered "pending".  What does that mean?
     116                 :             :  *
     117                 :             :  *   In general that means that if you set and then get the
     118                 :             :  *   transaction's or split's parameters inside the
     119                 :             :  *   BeginEdit/CommitEdit block, you'll get the values you just set.
     120                 :             :  *   However, if you change an object's many-to-one relationship with
     121                 :             :  *   another object, you won't see the change from the "many" side
     122                 :             :  *   until the CommitEdit.  For example, if you move a split from one
     123                 :             :  *   account into another, you can see the change with
     124                 :             :  *   xaccSplitGetAccount(), but both Accounts' split lists won't be
     125                 :             :  *   updated until the CommitEdit.  Correspondingly, no signals
     126                 :             :  *   (events) will be generated for those "foreign" objects, or the
     127                 :             :  *   Transaction, until the CommitEdit.
     128                 :             :  *
     129                 :             :  *   This behavior is important because, when we're finally ready to
     130                 :             :  *   commit to the backend, we can't be 100% sure that the backend
     131                 :             :  *   will still be available.  We have to offer the backend all of the
     132                 :             :  *   new state as if it were already "true", but we need to save all of
     133                 :             :  *   the old state in case the backend won't accept our commit.  If
     134                 :             :  *   the backend commit fails, we have to restore all the old state.
     135                 :             :  *   If the backend commit succeeds, and *only* after it succeeds, we
     136                 :             :  *   can advertise the new state to the rest of the engine (and gui).
     137                 :             :  *
     138                 :             :  *  Q: Who owns the ref of an added split if the Transaction is rolled
     139                 :             :  *  back?
     140                 :             :  *
     141                 :             :  *  A: This is a design decision.  If the answer is 'the user',
     142                 :             :  *  then the burden is on the api user to check the transaction after
     143                 :             :  *  every commit to see if the added split is really in the
     144                 :             :  *  transaction.  If they don't they risk leaking the split if the
     145                 :             :  *  commit was rolled back.  Another design is to answer 'the engine'.
     146                 :             :  *  In that case the burden is on the engine to free a newly added
     147                 :             :  *  split if the commit is rolled back.  Unfortunately the engine
     148                 :             :  *  objects aren't ref-counted, so this is tricky.
     149                 :             :  *
     150                 :             :  *  In the current implementation, the answer is 'the engine', but
     151                 :             :  *  that means that you must not add the split to two different
     152                 :             :  *  transactions during the begin/commit block, because if one rolls
     153                 :             :  *  back, they will both think they own the split.  This is one
     154                 :             :  *  specific example of the general problem that the outcome of two
     155                 :             :  *  parallel begin/commit edit blocks for two transactions where edits
     156                 :             :  *  for both transactions involve the same splits and one or more
     157                 :             :  *  edit-blocks is rolled-back, is poorly-defined.
     158                 :             :  *
     159                 :             :  *
     160                 :             :  *
     161                 :             :  * Design notes on event-generation: transaction-modified-events
     162                 :             :  * should not be generated until transaction commit or rollback
     163                 :             :  * time.  They should not be generated as each field is tweaked.
     164                 :             :  * This for two reasons:
     165                 :             :  * 1) Most editing events make multiple changes to a transaction,
     166                 :             :  *    which would generate a flurry of (needless) events, if they
     167                 :             :  *    weren't saved up till the commit.
     168                 :             :  * 2) Technically, its incorrect to use transaction data
     169                 :             :  *    until the transaction is committed.  The GUI element that
     170                 :             :  *    is changing the data can look at it, but all of the rest
     171                 :             :  *    of the GUI should ignore the data until its committed.
     172                 :             :  */
     173                 :             : 
     174                 :             : const char *trans_notes_str = "notes";
     175                 :             : const char *void_reason_str = "void-reason";
     176                 :             : const char *void_time_str = "void-time";
     177                 :             : const char *void_former_notes_str = "void-former-notes";
     178                 :             : const char *trans_is_closing_str = "book_closing";
     179                 :             : const char *doclink_uri_str = "assoc_uri"; // this is the old name for the document link, kept for compatibility
     180                 :             : 
     181                 :             : /* KVP entry for date-due value */
     182                 :             : #define TRANS_DATE_DUE_KVP       "trans-date-due"
     183                 :             : #define TRANS_TXN_TYPE_KVP       "trans-txn-type"
     184                 :             : #define TRANS_READ_ONLY_REASON   "trans-read-only"
     185                 :             : #define TRANS_REVERSED_BY        "reversed-by"
     186                 :             : #define GNC_SX_FROM              "from-sched-xaction"
     187                 :             : 
     188                 :             : #define ISO_DATELENGTH 32 /* length of an iso 8601 date string. */
     189                 :             : 
     190                 :             : /* This static indicates the debugging module that this .o belongs to.  */
     191                 :             : static QofLogModule log_module = GNC_MOD_ENGINE;
     192                 :             : 
     193                 :             : enum
     194                 :             : {
     195                 :             :     PROP_0,
     196                 :             :     PROP_CURRENCY,      /* Table */
     197                 :             :     PROP_NUM,           /* Table */
     198                 :             :     PROP_POST_DATE,     /* Table */
     199                 :             :     PROP_ENTER_DATE,    /* Table */
     200                 :             :     PROP_DESCRIPTION,   /* Table */
     201                 :             :     PROP_INVOICE,       /* KVP */
     202                 :             :     PROP_SX_TXN,        /* KVP */
     203                 :             :     PROP_ONLINE_ACCOUNT,/* KVP */
     204                 :             : };
     205                 :             : 
     206                 :             : void
     207                 :          15 : check_open (const Transaction *trans)
     208                 :             : {
     209                 :          15 :     if (trans && 0 >= qof_instance_get_editlevel(trans))
     210                 :           1 :         PERR ("transaction %p not open for editing", trans);
     211                 :          15 : }
     212                 :             : /********************************************************************\
     213                 :             : \********************************************************************/
     214                 :             : gboolean
     215                 :       23870 : xaccTransStillHasSplit(const Transaction *trans, const Split *s)
     216                 :             : {
     217                 :       23870 :     return (s && s->parent == trans && !qof_instance_get_destroying(s));
     218                 :             : }
     219                 :             : 
     220                 :             : /* Executes 'cmd_block' for each split currently in the transaction,
     221                 :             :  * using the in-edit state.  Use the variable 's' for each split. */
     222                 :             : #define FOR_EACH_SPLIT(trans, cmd_block) if (trans->splits) {                \
     223                 :             :         GList *splits;                                                  \
     224                 :             :         for (splits = (trans)->splits; splits; splits = splits->next) { \
     225                 :             :             Split *s = GNC_SPLIT(splits->data);                         \
     226                 :             :             if (xaccTransStillHasSplit(trans, s)) {                     \
     227                 :             :                 cmd_block;                                              \
     228                 :             :             }                                                           \
     229                 :             :         }                                                               \
     230                 :             :     }
     231                 :             : 
     232                 :             : static inline void mark_trans (Transaction *trans);
     233                 :        8792 : void mark_trans (Transaction *trans)
     234                 :             : {
     235                 :        9558 :     FOR_EACH_SPLIT(trans, mark_split(s));
     236                 :        8792 : }
     237                 :             : 
     238                 :             : static inline void gen_event_trans (Transaction *trans);
     239                 :        4579 : void gen_event_trans (Transaction *trans)
     240                 :             : {
     241                 :             :     GList *node;
     242                 :             : 
     243                 :       13952 :     for (node = trans->splits; node; node = node->next)
     244                 :             :     {
     245                 :        9373 :         Split *s = GNC_SPLIT(node->data);
     246                 :        9373 :         Account *account = s->acc;
     247                 :        9373 :         GNCLot *lot = s->lot;
     248                 :        9373 :         if (account)
     249                 :        9354 :             qof_event_gen (&account->inst, GNC_EVENT_ITEM_CHANGED, s);
     250                 :             : 
     251                 :        9373 :         if (lot)
     252                 :             :         {
     253                 :             :             /* A change of transaction date might affect opening date of lot */
     254                 :         287 :             qof_event_gen (QOF_INSTANCE(lot), QOF_EVENT_MODIFY, nullptr);
     255                 :             :         }
     256                 :             :     }
     257                 :        4579 : }
     258                 :             : 
     259                 :             : /* GObject Initialization */
     260                 :       31294 : G_DEFINE_TYPE(Transaction, gnc_transaction, QOF_TYPE_INSTANCE)
     261                 :             : 
     262                 :             : static void
     263                 :        8257 : gnc_transaction_init(Transaction* trans)
     264                 :             : {
     265                 :        8257 :     ENTER ("trans=%p", trans);
     266                 :             :     /* Fill in some sane defaults */
     267                 :        8257 :     trans->num         = CACHE_INSERT("");
     268                 :        8257 :     trans->description = CACHE_INSERT("");
     269                 :        8257 :     trans->common_currency = nullptr;
     270                 :        8257 :     trans->splits = nullptr;
     271                 :        8257 :     trans->date_entered  = 0;
     272                 :        8257 :     trans->date_posted  = 0;
     273                 :        8257 :     trans->marker = 0;
     274                 :        8257 :     trans->orig = nullptr;
     275                 :        8257 :     trans->txn_type = TXN_TYPE_UNCACHED;
     276                 :        8257 :     LEAVE (" ");
     277                 :        8257 : }
     278                 :             : 
     279                 :             : static void
     280                 :        7153 : gnc_transaction_dispose(GObject *txnp)
     281                 :             : {
     282                 :        7153 :     G_OBJECT_CLASS(gnc_transaction_parent_class)->dispose(txnp);
     283                 :        7153 : }
     284                 :             : 
     285                 :             : static void
     286                 :        7152 : gnc_transaction_finalize(GObject* txnp)
     287                 :             : {
     288                 :        7152 :     G_OBJECT_CLASS(gnc_transaction_parent_class)->finalize(txnp);
     289                 :        7152 : }
     290                 :             : 
     291                 :             : /* Note that g_value_set_object() refs the object, as does
     292                 :             :  * g_object_get(). But g_object_get() only unrefs once when it disgorges
     293                 :             :  * the object, leaving an unbalanced ref, which leaks. So instead of
     294                 :             :  * using g_value_set_object(), use g_value_take_object() which doesn't
     295                 :             :  * ref the object when used in get_property().
     296                 :             :  */
     297                 :             : static void
     298                 :        2039 : gnc_transaction_get_property(GObject* object,
     299                 :             :                              guint prop_id,
     300                 :             :                              GValue* value,
     301                 :             :                              GParamSpec* pspec)
     302                 :             : {
     303                 :             :     Transaction* tx;
     304                 :             :     Time64 time;
     305                 :             : 
     306                 :        2039 :     g_return_if_fail(GNC_IS_TRANSACTION(object));
     307                 :             : 
     308                 :        2039 :     tx = GNC_TRANSACTION(object);
     309                 :        2039 :     switch (prop_id)
     310                 :             :     {
     311                 :          10 :     case PROP_NUM:
     312                 :          10 :         g_value_set_string(value, tx->num);
     313                 :          10 :         break;
     314                 :          17 :     case PROP_DESCRIPTION:
     315                 :          17 :         g_value_set_string(value, tx->description);
     316                 :          17 :         break;
     317                 :          10 :     case PROP_CURRENCY:
     318                 :          10 :         g_value_take_object(value, tx->common_currency);
     319                 :          10 :         break;
     320                 :          10 :     case PROP_POST_DATE:
     321                 :          10 :         time.t = tx->date_posted;
     322                 :          10 :         g_value_set_boxed(value, &time);
     323                 :          10 :         break;
     324                 :          10 :     case PROP_ENTER_DATE:
     325                 :          10 :         time.t = tx->date_entered;
     326                 :          10 :         g_value_set_boxed(value, &time);
     327                 :          10 :         break;
     328                 :        1980 :     case PROP_INVOICE:
     329                 :        1980 :         qof_instance_get_kvp (QOF_INSTANCE (tx), value, 2, GNC_INVOICE_ID, GNC_INVOICE_GUID);
     330                 :        1980 :         break;
     331                 :           1 :     case PROP_SX_TXN:
     332                 :           1 :         qof_instance_get_kvp (QOF_INSTANCE (tx), value, 1, GNC_SX_FROM);
     333                 :           1 :         break;
     334                 :           1 :     case PROP_ONLINE_ACCOUNT:
     335                 :           1 :         qof_instance_get_kvp (QOF_INSTANCE (tx), value, 1, "online_id");
     336                 :           1 :         break;
     337                 :           0 :     default:
     338                 :           0 :         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
     339                 :           0 :         break;
     340                 :             :     }
     341                 :             : }
     342                 :             : 
     343                 :             : static void
     344                 :         129 : gnc_transaction_set_property(GObject* object,
     345                 :             :                              guint prop_id,
     346                 :             :                              const GValue* value,
     347                 :             :                              GParamSpec* pspec)
     348                 :             : {
     349                 :             :     Transaction* tx;
     350                 :             :     Time64 *t;
     351                 :             : 
     352                 :         129 :     g_return_if_fail(GNC_IS_TRANSACTION(object));
     353                 :             : 
     354                 :         129 :     tx = GNC_TRANSACTION(object);
     355                 :         129 :     g_assert (qof_instance_get_editlevel(tx));
     356                 :             : 
     357                 :         129 :     switch (prop_id)
     358                 :             :     {
     359                 :          10 :     case PROP_NUM:
     360                 :          10 :         xaccTransSetNum( tx, g_value_get_string(value));
     361                 :          10 :         break;
     362                 :          10 :     case PROP_DESCRIPTION:
     363                 :          10 :         xaccTransSetDescription(tx, g_value_get_string(value));
     364                 :          10 :         break;
     365                 :          10 :     case PROP_CURRENCY:
     366                 :          10 :         xaccTransSetCurrency(tx, GNC_COMMODITY(g_value_get_object(value)));
     367                 :          10 :         break;
     368                 :          10 :     case PROP_POST_DATE:
     369                 :          10 :         t = (Time64*)g_value_get_boxed(value);
     370                 :          10 :         xaccTransSetDatePostedSecs(tx, t->t);
     371                 :          10 :         break;
     372                 :          10 :     case PROP_ENTER_DATE:
     373                 :          10 :         t = (Time64*)g_value_get_boxed(value);
     374                 :          10 :         xaccTransSetDateEnteredSecs(tx, t->t);
     375                 :          10 :         break;
     376                 :          66 :     case PROP_INVOICE:
     377                 :          66 :         qof_instance_set_kvp (QOF_INSTANCE (tx), value, 2, GNC_INVOICE_ID, GNC_INVOICE_GUID);
     378                 :          66 :         break;
     379                 :           5 :     case PROP_SX_TXN:
     380                 :           5 :         qof_instance_set_kvp (QOF_INSTANCE (tx), value, 1, GNC_SX_FROM);
     381                 :           5 :         break;
     382                 :           8 :     case PROP_ONLINE_ACCOUNT:
     383                 :           8 :         qof_instance_set_kvp (QOF_INSTANCE (tx), value, 1, "online_id");
     384                 :           8 :         break;
     385                 :           0 :     default:
     386                 :           0 :         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
     387                 :           0 :         break;
     388                 :             :     }
     389                 :             : }
     390                 :             : 
     391                 :             : static void
     392                 :          41 : gnc_transaction_class_init(TransactionClass* klass)
     393                 :             : {
     394                 :          41 :     GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
     395                 :             : 
     396                 :          41 :     gobject_class->dispose = gnc_transaction_dispose;
     397                 :          41 :     gobject_class->finalize = gnc_transaction_finalize;
     398                 :          41 :     gobject_class->set_property = gnc_transaction_set_property;
     399                 :          41 :     gobject_class->get_property = gnc_transaction_get_property;
     400                 :             : 
     401                 :             :     g_object_class_install_property
     402                 :          41 :     (gobject_class,
     403                 :             :      PROP_NUM,
     404                 :             :      g_param_spec_string("num",
     405                 :             :                          "Transaction Number",
     406                 :             :                          "The transactionNumber is an arbitrary string "
     407                 :             :                          "assigned by the user.  It is intended to be "
     408                 :             :                          "a short 1-6 character string that is displayed "
     409                 :             :                          "by the register.  For checks, it is usually the "
     410                 :             :                          "check number.  For other types of transactions, "
     411                 :             :                          "it can be any string.",
     412                 :             :                          nullptr,
     413                 :             :                          G_PARAM_READWRITE));
     414                 :             : 
     415                 :             :     g_object_class_install_property
     416                 :          41 :     (gobject_class,
     417                 :             :      PROP_DESCRIPTION,
     418                 :             :      g_param_spec_string("description",
     419                 :             :                          "Transaction Description",
     420                 :             :                          "The transaction description is an arbitrary string "
     421                 :             :                          "assigned by the user.  It is usually the customer, "
     422                 :             :                          "vendor or other organization associated with the "
     423                 :             :                          "transaction.",
     424                 :             :                          nullptr,
     425                 :             :                          G_PARAM_READWRITE));
     426                 :             : 
     427                 :             :     g_object_class_install_property
     428                 :          41 :     (gobject_class,
     429                 :             :      PROP_CURRENCY,
     430                 :             :      g_param_spec_object ("currency",
     431                 :             :                           "Currency",
     432                 :             :                           "The base currency for this transaction.",
     433                 :             :                           GNC_TYPE_COMMODITY,
     434                 :             :                           G_PARAM_READWRITE));
     435                 :             : 
     436                 :             :     g_object_class_install_property
     437                 :          41 :     (gobject_class,
     438                 :             :      PROP_POST_DATE,
     439                 :             :      g_param_spec_boxed("post-date",
     440                 :             :                         "Post Date",
     441                 :             :                         "The date the transaction occurred.",
     442                 :             :                         GNC_TYPE_TIME64,
     443                 :             :                         G_PARAM_READWRITE));
     444                 :             : 
     445                 :             :     g_object_class_install_property
     446                 :          41 :     (gobject_class,
     447                 :             :      PROP_ENTER_DATE,
     448                 :             :      g_param_spec_boxed("enter-date",
     449                 :             :                         "Enter Date",
     450                 :             :                         "The date the transaction was entered.",
     451                 :             :                         GNC_TYPE_TIME64,
     452                 :             :                         G_PARAM_READWRITE));
     453                 :             : 
     454                 :          41 :      g_object_class_install_property(
     455                 :             :        gobject_class,
     456                 :             :         PROP_INVOICE,
     457                 :             :         g_param_spec_boxed("invoice",
     458                 :             :                            "Invoice attached to lot",
     459                 :             :                            "Used by GncInvoice",
     460                 :             :                            GNC_TYPE_GUID,
     461                 :             :                            G_PARAM_READWRITE));
     462                 :             : 
     463                 :          41 :      g_object_class_install_property(
     464                 :             :        gobject_class,
     465                 :             :         PROP_SX_TXN,
     466                 :             :         g_param_spec_boxed("from-sched-xaction",
     467                 :             :                            "From Scheduled Transaction",
     468                 :             :                            "Used by Scheduled Transastions to record the "
     469                 :             :                            "originating template transaction for created "
     470                 :             :                            "transactions",
     471                 :             :                            GNC_TYPE_GUID,
     472                 :             :                            G_PARAM_READWRITE));
     473                 :             : 
     474                 :             :     g_object_class_install_property
     475                 :          41 :     (gobject_class,
     476                 :             :      PROP_ONLINE_ACCOUNT,
     477                 :             :      g_param_spec_string ("online-id",
     478                 :             :                           "Online Account ID",
     479                 :             :                           "The online account which corresponds to this "
     480                 :             :                           "account for OFX/HCBI import",
     481                 :             :                           nullptr,
     482                 :             :                           G_PARAM_READWRITE));
     483                 :          41 : }
     484                 :             : 
     485                 :             : /********************************************************************\
     486                 :             :  * xaccInitTransaction
     487                 :             :  * Initialize a transaction structure
     488                 :             : \********************************************************************/
     489                 :             : 
     490                 :             : static void
     491                 :        3443 : xaccInitTransaction (Transaction * trans, QofBook *book)
     492                 :             : {
     493                 :        3443 :     ENTER ("trans=%p", trans);
     494                 :        3443 :     qof_instance_init_data (&trans->inst, GNC_ID_TRANS, book);
     495                 :        3443 :     LEAVE (" ");
     496                 :        3443 : }
     497                 :             : 
     498                 :             : /********************************************************************\
     499                 :             : \********************************************************************/
     500                 :             : 
     501                 :             : Transaction *
     502                 :        3444 : xaccMallocTransaction (QofBook *book)
     503                 :             : {
     504                 :             :     Transaction *trans;
     505                 :             : 
     506                 :        3444 :     g_return_val_if_fail (book, nullptr);
     507                 :             : 
     508                 :        3443 :     trans = GNC_TRANSACTION(g_object_new(GNC_TYPE_TRANSACTION, nullptr));
     509                 :        3443 :     xaccInitTransaction (trans, book);
     510                 :        3443 :     qof_event_gen (&trans->inst, QOF_EVENT_CREATE, nullptr);
     511                 :             : 
     512                 :        3443 :     return trans;
     513                 :             : }
     514                 :             : 
     515                 :             : #ifdef DUMP_FUNCTIONS
     516                 :             : /* Please don't delete this function.  Although it is not called by
     517                 :             :    any other code in GnuCash, it is useful when debugging.  For example
     518                 :             :    it can be called using the gdb "call" command when stopped at a
     519                 :             :    breakpoint.  */
     520                 :             : void
     521                 :             : xaccTransDump (const Transaction *trans, const char *tag)
     522                 :             : {
     523                 :             :     GList *node;
     524                 :             :     char datebuff[MAX_DATE_LENGTH + 1];
     525                 :             : 
     526                 :             :     printf("%s Trans %p", tag, trans);
     527                 :             :     memset(datebuff, 0, sizeof(datebuff));
     528                 :             :     qof_print_date_buff(datebuff, MAX_DATE_LENGTH, trans->date_entered);
     529                 :             :     printf("    Entered:     %s\n", datebuff);
     530                 :             :     memset(datebuff, 0, sizeof(datebuff));
     531                 :             :     qof_print_date_buff(datebuff, MAX_DATE_LENGTH, trans->date_posted);
     532                 :             :     printf("    Posted:      %s\n", datebuff);
     533                 :             :     printf("    Num:         %s\n", trans->num ? trans->num : "(null)");
     534                 :             :     printf("    Description: %s\n",
     535                 :             :            trans->description ? trans->description : "(null)");
     536                 :             :     printf("    Currency:    %s\n",
     537                 :             :            gnc_commodity_get_printname(trans->common_currency));
     538                 :             :     printf("    version:     %x\n", qof_instance_get_version(trans));
     539                 :             :     printf("    version_chk: %x\n", qof_instance_get_version_check(trans));
     540                 :             :     printf("    editlevel:   %x\n", qof_instance_get_editlevel(trans));
     541                 :             :     printf("    orig:        %p\n", trans->orig);
     542                 :             :     printf("    idata:       %x\n", qof_instance_get_idata(trans));
     543                 :             :     printf("    splits:      ");
     544                 :             :     for (node = trans->splits; node; node = node->next)
     545                 :             :     {
     546                 :             :         printf("%p ", node->data);
     547                 :             :     }
     548                 :             :     printf("\n");
     549                 :             :     for (node = trans->splits; node; node = node->next)
     550                 :             :     {
     551                 :             :         xaccSplitDump(GNC_SPLIT(node->data), tag);
     552                 :             :     }
     553                 :             :     printf("\n");
     554                 :             : }
     555                 :             : #endif
     556                 :             : 
     557                 :             : void
     558                 :        4570 : xaccTransSortSplits (Transaction *trans)
     559                 :             : {
     560                 :        4570 :     GList *node, *new_list = nullptr;
     561                 :             :     Split *split;
     562                 :             : 
     563                 :             :     /* first debits */
     564                 :       13936 :     for (node = trans->splits; node; node = node->next)
     565                 :             :     {
     566                 :        9366 :         split = GNC_SPLIT(node->data);
     567                 :        9366 :         if (gnc_numeric_negative_p (xaccSplitGetValue(split)))
     568                 :        4352 :             continue;
     569                 :        5014 :         new_list = g_list_prepend (new_list, split);
     570                 :             :     }
     571                 :             : 
     572                 :             :     /* then credits */
     573                 :       13936 :     for (node = trans->splits; node; node = node->next)
     574                 :             :     {
     575                 :        9366 :         split = GNC_SPLIT(node->data);
     576                 :        9366 :         if (!gnc_numeric_negative_p (xaccSplitGetValue(split)))
     577                 :        5014 :             continue;
     578                 :        4352 :         new_list = g_list_prepend (new_list, split);
     579                 :             :     }
     580                 :             : 
     581                 :             :     /* install newly sorted list */
     582                 :        4570 :     g_list_free(trans->splits);
     583                 :        4570 :     trans->splits = g_list_reverse (new_list);
     584                 :        4570 : }
     585                 :             : 
     586                 :             : 
     587                 :             : /********************************************************************\
     588                 :             : \********************************************************************/
     589                 :             : /* This routine is not exposed externally, since it does weird things,
     590                 :             :  * like not really owning the splits correctly, and other weirdnesses.
     591                 :             :  * This routine is prone to programmer snafu if not used correctly.
     592                 :             :  * It is used only by the edit-rollback code.
     593                 :             :  */
     594                 :             : static Transaction *
     595                 :        4799 : dupe_trans (const Transaction *from)
     596                 :             : {
     597                 :             :     Transaction *to;
     598                 :        4799 :     to = GNC_TRANSACTION(g_object_new (GNC_TYPE_TRANSACTION, nullptr));
     599                 :             : 
     600                 :        4799 :     CACHE_REPLACE (to->num, from->num);
     601                 :        4799 :     CACHE_REPLACE (to->description, from->description);
     602                 :             : 
     603                 :        4799 :     to->splits = g_list_copy_deep (from->splits, (GCopyFunc)xaccDupeSplit, nullptr);
     604                 :        4799 :     to->date_entered = from->date_entered;
     605                 :        4799 :     to->date_posted = from->date_posted;
     606                 :        4799 :     qof_instance_copy_version(to, from);
     607                 :        4799 :     to->orig = nullptr;
     608                 :             : 
     609                 :        4799 :     to->common_currency = from->common_currency;
     610                 :             : 
     611                 :             :     /* Trash the guid and entity table. We don't want to mistake
     612                 :             :      * the cloned transaction as something official.  If we ever
     613                 :             :      * use this transaction, we'll have to fix this up.
     614                 :             :      */
     615                 :        4799 :     to->inst.e_type = nullptr;
     616                 :        4799 :     qof_instance_set_guid(to, guid_null());
     617                 :        4799 :     qof_instance_copy_book(to, from);
     618                 :        4799 :     qof_instance_copy_kvp (QOF_INSTANCE(to), QOF_INSTANCE(from));
     619                 :             : 
     620                 :        4799 :     return to;
     621                 :             : }
     622                 :             : 
     623                 :             : /********************************************************************\
     624                 :             :  * Use this routine to externally duplicate a transaction.  It creates
     625                 :             :  * a full fledged transaction with unique guid, splits, etc. and
     626                 :             :  * writes it to the database.
     627                 :             : \********************************************************************/
     628                 :             : Transaction *
     629                 :          11 : xaccTransCloneNoKvp (const Transaction *from)
     630                 :             : {
     631                 :             :     Transaction *to;
     632                 :             :     Split *split;
     633                 :             :     GList *node;
     634                 :             : 
     635                 :          11 :     qof_event_suspend();
     636                 :          11 :     to = GNC_TRANSACTION(g_object_new (GNC_TYPE_TRANSACTION, nullptr));
     637                 :             : 
     638                 :          11 :     to->date_entered    = from->date_entered;
     639                 :          11 :     to->date_posted     = from->date_posted;
     640                 :          11 :     CACHE_REPLACE (to->num, from->num);
     641                 :          11 :     CACHE_REPLACE (to->description, from->description);
     642                 :          11 :     to->common_currency = from->common_currency;
     643                 :          11 :     qof_instance_copy_version(to, from);
     644                 :          11 :     qof_instance_copy_version_check(to, from);
     645                 :             : 
     646                 :          11 :     to->orig            = nullptr;
     647                 :             : 
     648                 :          11 :     qof_instance_init_data (&to->inst, GNC_ID_TRANS,
     649                 :             :                             qof_instance_get_book(from));
     650                 :             : 
     651                 :          11 :     xaccTransBeginEdit(to);
     652                 :          32 :     for (node = from->splits; node; node = node->next)
     653                 :             :     {
     654                 :          21 :         split = xaccSplitCloneNoKvp(GNC_SPLIT(node->data));
     655                 :          21 :         split->parent = to;
     656                 :          21 :         to->splits = g_list_append (to->splits, split);
     657                 :             :     }
     658                 :          11 :     qof_instance_set_dirty(QOF_INSTANCE(to));
     659                 :          11 :     xaccTransCommitEdit(to);
     660                 :          11 :     qof_event_resume();
     661                 :             : 
     662                 :          11 :     return to;
     663                 :             : }
     664                 :             : 
     665                 :             : Transaction *
     666                 :           7 : xaccTransClone (const Transaction *from)
     667                 :             : {
     668                 :           7 :     Transaction *to = xaccTransCloneNoKvp (from);
     669                 :             : 
     670                 :           7 :     if (g_list_length (to->splits) != g_list_length (from->splits))
     671                 :             :     {
     672                 :           0 :         PERR ("Cloned transaction has different number of splits from original");
     673                 :           0 :         xaccTransDestroy (to);
     674                 :           0 :         return nullptr;
     675                 :             :     }
     676                 :             : 
     677                 :           7 :     xaccTransBeginEdit (to);
     678                 :           7 :     qof_instance_copy_kvp (QOF_INSTANCE (to), QOF_INSTANCE (from));
     679                 :             : 
     680                 :             :     /* But not the online-id! */
     681                 :           7 :     qof_instance_set (QOF_INSTANCE (to), "online-id", nullptr, nullptr);
     682                 :             : 
     683                 :          20 :     for (GList* lfrom = from->splits, *lto = to->splits; lfrom && lto;
     684                 :          13 :          lfrom = g_list_next (lfrom), lto = g_list_next (lto))
     685                 :          13 :         xaccSplitCopyKvp (GNC_SPLIT(lfrom->data), GNC_SPLIT(lto->data));
     686                 :             : 
     687                 :           7 :     xaccTransCommitEdit (to);
     688                 :           7 :     return to;
     689                 :             : }
     690                 :             : 
     691                 :             : /*################## Added for Reg2 #################*/
     692                 :             : 
     693                 :             : /********************************************************************\
     694                 :             :  * Copy a transaction to the 'clipboard' transaction using
     695                 :             :  *  dupe_trans. The 'clipboard' transaction must never
     696                 :             :  *  be dereferenced.
     697                 :             : \********************************************************************/
     698                 :           0 : Transaction * xaccTransCopyToClipBoard(const Transaction *from_trans)
     699                 :             : {
     700                 :             :     Transaction *to_trans;
     701                 :             : 
     702                 :           0 :     if (!from_trans)
     703                 :           0 :         return nullptr;
     704                 :             : 
     705                 :           0 :     to_trans = dupe_trans(from_trans);
     706                 :           0 :     return to_trans;
     707                 :             : }
     708                 :             : 
     709                 :             : /********************************************************************\
     710                 :             :  * Copy a transaction to another using the function below without
     711                 :             :  *  changing any account information.
     712                 :             : \********************************************************************/
     713                 :             : void
     714                 :           0 : xaccTransCopyOnto(const Transaction *from_trans, Transaction *to_trans)
     715                 :             : {
     716                 :           0 :     xaccTransCopyFromClipBoard(from_trans, to_trans, nullptr, nullptr, TRUE);
     717                 :           0 : }
     718                 :             : 
     719                 :             : /********************************************************************\
     720                 :             :  * This function explicitly must robustly handle some unusual input.
     721                 :             :  *
     722                 :             :  *  'from_trans' may be a duped trans (see dupe_trans), so its
     723                 :             :  *   splits may not really belong to the accounts that they say they do.
     724                 :             :  *
     725                 :             :  *  'from_acc' need not be a valid account. It may be an already freed
     726                 :             :  *   Account. Therefore, it must not be dereferenced at all.
     727                 :             :  *
     728                 :             :  *   Neither 'from_trans', nor 'from_acc', nor any of 'from's splits may
     729                 :             :  *   be modified in any way.
     730                 :             :  *
     731                 :             :  *   'no_date' if TRUE will not copy the date posted.
     732                 :             :  *
     733                 :             :  *   The 'to_trans' transaction will end up with valid copies of from's
     734                 :             :  *   splits.  In addition, the copies of any of from's splits that were
     735                 :             :  *   in from_acc (or at least claimed to be) will end up in to_acc.
     736                 :             : \********************************************************************/
     737                 :             : void
     738                 :           2 : xaccTransCopyFromClipBoard(const Transaction *from_trans, Transaction *to_trans,
     739                 :             :                            const Account *from_acc, Account *to_acc, gboolean no_date)
     740                 :             : {
     741                 :           2 :     gboolean change_accounts = FALSE;
     742                 :             :     GList *node;
     743                 :             : 
     744                 :           2 :     if (!from_trans || !to_trans)
     745                 :           0 :         return;
     746                 :             : 
     747                 :           2 :     change_accounts = from_acc && GNC_IS_ACCOUNT(to_acc) && from_acc != to_acc;
     748                 :           2 :     xaccTransBeginEdit(to_trans);
     749                 :             : 
     750                 :           2 :     xaccTransClearSplits(to_trans);
     751                 :           2 :     xaccTransSetCurrency(to_trans, xaccTransGetCurrency(from_trans));
     752                 :           2 :     xaccTransSetDescription(to_trans, xaccTransGetDescription(from_trans));
     753                 :             : 
     754                 :           2 :     if ((xaccTransGetNum(to_trans) == nullptr) || (g_strcmp0 (xaccTransGetNum(to_trans), "") == 0))
     755                 :           2 :         xaccTransSetNum(to_trans, xaccTransGetNum(from_trans));
     756                 :             : 
     757                 :           2 :     xaccTransSetNotes(to_trans, xaccTransGetNotes(from_trans));
     758                 :           2 :     xaccTransSetDocLink(to_trans, xaccTransGetDocLink (from_trans));
     759                 :           2 :     if(!no_date)
     760                 :             :     {
     761                 :           1 :         xaccTransSetDatePostedSecs(to_trans, xaccTransRetDatePosted (from_trans));
     762                 :             :     }
     763                 :             : 
     764                 :             :     /* Each new split will be parented to 'to' */
     765                 :           6 :     for (node = from_trans->splits; node; node = node->next)
     766                 :             :     {
     767                 :           4 :         Split *new_split = xaccMallocSplit( qof_instance_get_book(QOF_INSTANCE(from_trans)));
     768                 :           4 :         xaccSplitCopyOnto(GNC_SPLIT(node->data), new_split);
     769                 :           4 :         if (change_accounts && xaccSplitGetAccount(GNC_SPLIT(node->data)) == from_acc)
     770                 :           2 :             xaccSplitSetAccount(new_split, to_acc);
     771                 :           4 :         xaccSplitSetParent(new_split, to_trans);
     772                 :             :     }
     773                 :           2 :     xaccTransCommitEdit(to_trans);
     774                 :             : }
     775                 :             : 
     776                 :             : /*################## Added for Reg2 #################*/
     777                 :             : 
     778                 :             : /********************************************************************\
     779                 :             :  Free the transaction.
     780                 :             : \********************************************************************/
     781                 :             : static void
     782                 :        7067 : xaccFreeTransaction (Transaction *trans)
     783                 :             : {
     784                 :        7067 :     if (!trans) return;
     785                 :             : 
     786                 :        7067 :     ENTER ("(addr=%p)", trans);
     787                 :        7067 :     if (((char *) 1) == trans->num)
     788                 :             :     {
     789                 :           0 :         PERR ("double-free %p", trans);
     790                 :           0 :         LEAVE (" ");
     791                 :           0 :         return;
     792                 :             :     }
     793                 :             : 
     794                 :             :     /* free up the destination splits */
     795                 :        7067 :     g_list_free_full (trans->splits, (GDestroyNotify)xaccFreeSplit);
     796                 :        7067 :     trans->splits = nullptr;
     797                 :             : 
     798                 :             :     /* free up transaction strings */
     799                 :        7067 :     CACHE_REMOVE(trans->num);
     800                 :        7067 :     CACHE_REMOVE(trans->description);
     801                 :             : 
     802                 :             :     /* Just in case someone looks up freed memory ... */
     803                 :        7067 :     trans->num         = (char *) 1;
     804                 :        7067 :     trans->description = nullptr;
     805                 :        7067 :     trans->date_entered = 0;
     806                 :        7067 :     trans->date_posted = 0;
     807                 :        7067 :     if (trans->orig)
     808                 :             :     {
     809                 :         116 :         xaccFreeTransaction (trans->orig);
     810                 :         116 :         trans->orig = nullptr;
     811                 :             :     }
     812                 :             : 
     813                 :             :     /* qof_instance_release (&trans->inst); */
     814                 :        7067 :     g_object_unref(trans);
     815                 :             : 
     816                 :        7067 :     LEAVE ("(addr=%p)", trans);
     817                 :             : }
     818                 :             : 
     819                 :             : /********************************************************************
     820                 :             :  xaccTransEqual
     821                 :             : 
     822                 :             :  Compare two transactions for equality.  We don't pay any attention to
     823                 :             :  rollback issues here, and we only care about equality of "permanent
     824                 :             :  fields", basically the things that would survive a file save/load
     825                 :             :  cycle.
     826                 :             : 
     827                 :             :  ********************************************************************/
     828                 :             : 
     829                 :             : /* return 0 when splits have equal guids */
     830                 :             : static gint
     831                 :         201 : compare_split_guids (gconstpointer a, gconstpointer b)
     832                 :             : {
     833                 :         201 :     const Split *sa = GNC_SPLIT(a);
     834                 :         201 :     const Split *sb = GNC_SPLIT(b);
     835                 :             : 
     836                 :         201 :     if (sa == sb) return 0;
     837                 :         201 :     if (!sa || !sb) return 1;
     838                 :             : 
     839                 :         201 :     return guid_compare (xaccSplitGetGUID (sa), xaccSplitGetGUID (sb));
     840                 :             : }
     841                 :             : 
     842                 :             : gboolean
     843                 :         268 : xaccTransEqual(const Transaction *ta, const Transaction *tb,
     844                 :             :                gboolean check_guids,
     845                 :             :                gboolean check_splits,
     846                 :             :                gboolean check_balances,
     847                 :             :                gboolean assume_ordered)
     848                 :             : {
     849                 :             :     gboolean same_book;
     850                 :             : 
     851                 :         268 :     if (!ta && !tb) return TRUE; /* Arguable.  FALSE may be better. */
     852                 :             : 
     853                 :         267 :     if (!ta || !tb)
     854                 :             :     {
     855                 :           4 :         PINFO ("one is nullptr");
     856                 :           4 :         return FALSE;
     857                 :             :     }
     858                 :             : 
     859                 :         263 :     if (ta == tb) return TRUE;
     860                 :             : 
     861                 :         253 :     same_book = qof_instance_get_book(QOF_INSTANCE(ta)) == qof_instance_get_book(QOF_INSTANCE(tb));
     862                 :             : 
     863                 :         253 :     if (check_guids)
     864                 :             :     {
     865                 :         225 :         if (qof_instance_guid_compare(ta, tb) != 0)
     866                 :             :         {
     867                 :           3 :             PINFO ("GUIDs differ");
     868                 :           3 :             return FALSE;
     869                 :             :         }
     870                 :             :     }
     871                 :             : 
     872                 :         250 :     if (!gnc_commodity_equal(ta->common_currency, tb->common_currency))
     873                 :             :     {
     874                 :           1 :         PINFO ("commodities differ %s vs %s",
     875                 :             :                gnc_commodity_get_unique_name (ta->common_currency),
     876                 :             :                gnc_commodity_get_unique_name (tb->common_currency));
     877                 :           1 :         return FALSE;
     878                 :             :     }
     879                 :             : 
     880                 :         249 :     if (ta->date_entered != tb->date_entered)
     881                 :             :     {
     882                 :             :         char buf1[100];
     883                 :             :         char buf2[100];
     884                 :             : 
     885                 :           1 :         (void)gnc_time64_to_iso8601_buff(ta->date_entered, buf1);
     886                 :           1 :         (void)gnc_time64_to_iso8601_buff(tb->date_entered, buf2);
     887                 :           1 :         PINFO ("date entered differs: '%s' vs '%s'", buf1, buf2);
     888                 :           1 :         return FALSE;
     889                 :             :     }
     890                 :             : 
     891                 :         248 :     if (ta->date_posted != tb->date_posted)
     892                 :             :     {
     893                 :             :         char buf1[100];
     894                 :             :         char buf2[100];
     895                 :             : 
     896                 :           1 :         (void)gnc_time64_to_iso8601_buff(ta->date_posted, buf1);
     897                 :           1 :         (void)gnc_time64_to_iso8601_buff(tb->date_posted, buf2);
     898                 :           1 :         PINFO ("date posted differs: '%s' vs '%s'", buf1, buf2);
     899                 :           1 :         return FALSE;
     900                 :             :     }
     901                 :             : 
     902                 :             :     /* If the same book, since we use cached strings, we can just compare pointer
     903                 :             :      * equality for num and description
     904                 :             :      */
     905                 :         247 :     if ((same_book && ta->num != tb->num) || (!same_book && g_strcmp0(ta->num, tb->num) != 0))
     906                 :             :     {
     907                 :           2 :         PINFO ("num differs: %s vs %s", ta->num, tb->num);
     908                 :           2 :         return FALSE;
     909                 :             :     }
     910                 :             : 
     911                 :         169 :     if ((same_book && ta->description != tb->description)
     912                 :         414 :             || (!same_book && g_strcmp0(ta->description, tb->description)))
     913                 :             :     {
     914                 :           2 :         PINFO ("descriptions differ: %s vs %s", ta->description, tb->description);
     915                 :           2 :         return FALSE;
     916                 :             :     }
     917                 :             : 
     918                 :         243 :     if (qof_instance_compare_kvp (QOF_INSTANCE (ta), QOF_INSTANCE (tb)) != 0)
     919                 :             :     {
     920                 :             :         char *frame_a;
     921                 :             :         char *frame_b;
     922                 :             : 
     923                 :           1 :         frame_a = qof_instance_kvp_as_string (QOF_INSTANCE (ta));
     924                 :           1 :         frame_b = qof_instance_kvp_as_string (QOF_INSTANCE (tb));
     925                 :             : 
     926                 :             : 
     927                 :           1 :         PINFO ("kvp frames differ:\n%s\n\nvs\n\n%s", frame_a, frame_b);
     928                 :             : 
     929                 :           1 :         g_free (frame_a);
     930                 :           1 :         g_free (frame_b);
     931                 :             : 
     932                 :           1 :         return FALSE;
     933                 :             :     }
     934                 :             : 
     935                 :         242 :     if (check_splits)
     936                 :             :     {
     937                 :          75 :         if ((!ta->splits && tb->splits) || (!tb->splits && ta->splits))
     938                 :             :         {
     939                 :           0 :             PINFO ("only one has splits");
     940                 :           0 :             return FALSE;
     941                 :             :         }
     942                 :             : 
     943                 :          75 :         if (ta->splits && tb->splits)
     944                 :             :         {
     945                 :             :             GList *node_a, *node_b;
     946                 :             : 
     947                 :          75 :             for (node_a = ta->splits, node_b = tb->splits;
     948                 :         219 :                     node_a;
     949                 :         144 :                     node_a = node_a->next, node_b = node_b->next)
     950                 :             :             {
     951                 :         147 :                 Split *split_a = GNC_SPLIT(node_a->data);
     952                 :             :                 Split *split_b;
     953                 :             : 
     954                 :             :                 /* don't presume that the splits are in the same order */
     955                 :         147 :                 if (!assume_ordered)
     956                 :         134 :                     node_b = g_list_find_custom (tb->splits, split_a,
     957                 :             :                                                  compare_split_guids);
     958                 :             : 
     959                 :         147 :                 if (!node_b)
     960                 :             :                 {
     961                 :             :                     gchar guidstr[GUID_ENCODING_LENGTH+1];
     962                 :           0 :                     guid_to_string_buff (xaccSplitGetGUID (split_a),guidstr);
     963                 :             : 
     964                 :           0 :                     PINFO ("first has split %s and second does not",guidstr);
     965                 :           0 :                     return FALSE;
     966                 :             :                 }
     967                 :             : 
     968                 :         147 :                 split_b = GNC_SPLIT(node_b->data);
     969                 :             : 
     970                 :         147 :                 if (!xaccSplitEqual (split_a, split_b, check_guids, check_balances,
     971                 :             :                                      FALSE))
     972                 :             :                 {
     973                 :             :                     char str_a[GUID_ENCODING_LENGTH + 1];
     974                 :             :                     char str_b[GUID_ENCODING_LENGTH + 1];
     975                 :             : 
     976                 :           3 :                     guid_to_string_buff (xaccSplitGetGUID (split_a), str_a);
     977                 :           3 :                     guid_to_string_buff (xaccSplitGetGUID (split_b), str_b);
     978                 :             : 
     979                 :           3 :                     PINFO ("splits %s and %s differ", str_a, str_b);
     980                 :           3 :                     return FALSE;
     981                 :             :                 }
     982                 :             :             }
     983                 :             : 
     984                 :          72 :             if (g_list_length (ta->splits) != g_list_length (tb->splits))
     985                 :             :             {
     986                 :           0 :                 PINFO ("different number of splits");
     987                 :           0 :                 return FALSE;
     988                 :             :             }
     989                 :             :         }
     990                 :             :     }
     991                 :             : 
     992                 :         239 :     return TRUE;
     993                 :             : }
     994                 :             : 
     995                 :             : /********************************************************************\
     996                 :             : xaccTransUseTradingAccounts
     997                 :             : 
     998                 :             : Returns true if the transaction should include trading account splits if
     999                 :             : it involves more than one commodity.
    1000                 :             : \********************************************************************/
    1001                 :             : 
    1002                 :        6027 : gboolean xaccTransUseTradingAccounts(const Transaction *trans)
    1003                 :             : {
    1004                 :        6027 :     return qof_book_use_trading_accounts(qof_instance_get_book (trans));
    1005                 :             : }
    1006                 :             : 
    1007                 :             : /********************************************************************\
    1008                 :             : \********************************************************************/
    1009                 :             : 
    1010                 :             : Transaction *
    1011                 :          58 : xaccTransLookup (const GncGUID *guid, QofBook *book)
    1012                 :             : {
    1013                 :             :     QofCollection *col;
    1014                 :          58 :     if (!guid || !book) return nullptr;
    1015                 :          58 :     col = qof_book_get_collection (book, GNC_ID_TRANS);
    1016                 :          58 :     return (Transaction *) qof_collection_lookup_entity (col, guid);
    1017                 :             : }
    1018                 :             : 
    1019                 :             : /********************************************************************\
    1020                 :             : \********************************************************************/
    1021                 :             : 
    1022                 :             : gnc_numeric
    1023                 :        3042 : xaccTransGetImbalanceValue (const Transaction * trans)
    1024                 :             : {
    1025                 :        3042 :     gnc_numeric imbal = gnc_numeric_zero();
    1026                 :        3042 :     if (!trans) return imbal;
    1027                 :             : 
    1028                 :        3042 :     ENTER("(trans=%p)", trans);
    1029                 :             :     /* Could use xaccSplitsComputeValue, except that we want to use
    1030                 :             :        GNC_HOW_DENOM_EXACT */
    1031                 :        9214 :     FOR_EACH_SPLIT(trans, imbal =
    1032                 :             :                        gnc_numeric_add(imbal, xaccSplitGetValue(s),
    1033                 :             :                                        GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT));
    1034                 :        3042 :     LEAVE("(trans=%p) imbal=%s", trans, gnc_num_dbg_to_string(imbal));
    1035                 :        3042 :     return imbal;
    1036                 :             : }
    1037                 :             : 
    1038                 :             : MonetaryList *
    1039                 :          13 : xaccTransGetImbalance (const Transaction * trans)
    1040                 :             : {
    1041                 :             :     /* imbal_value is used if either (1) the transaction has a non currency
    1042                 :             :        split or (2) all the splits are in the same currency.  If there are
    1043                 :             :        no non-currency splits and not all splits are in the same currency then
    1044                 :             :        imbal_list is used to compute the imbalance. */
    1045                 :          13 :     MonetaryList *imbal_list = nullptr;
    1046                 :          13 :     gnc_numeric imbal_value = gnc_numeric_zero();
    1047                 :             :     gboolean trading_accts;
    1048                 :             : 
    1049                 :          13 :     if (!trans) return imbal_list;
    1050                 :             : 
    1051                 :          12 :     ENTER("(trans=%p)", trans);
    1052                 :             : 
    1053                 :          12 :     trading_accts = xaccTransUseTradingAccounts (trans);
    1054                 :             : 
    1055                 :             :     /* If using trading accounts and there is at least one split that is not
    1056                 :             :        in the transaction currency or a split that has a price or exchange
    1057                 :             :        rate other than 1, then compute the balance in each commodity in the
    1058                 :             :        transaction.  Otherwise (all splits are in the transaction's currency)
    1059                 :             :        then compute the balance using the value fields.
    1060                 :             : 
    1061                 :             :        Optimize for the common case of only one currency and a balanced
    1062                 :             :        transaction. */
    1063                 :          50 :     FOR_EACH_SPLIT(trans,
    1064                 :             :     {
    1065                 :             :         gnc_commodity *commodity;
    1066                 :             :         commodity = xaccAccountGetCommodity(xaccSplitGetAccount(s));
    1067                 :             :         if (trading_accts &&
    1068                 :             :         (imbal_list ||
    1069                 :             :         ! gnc_commodity_equiv(commodity, trans->common_currency) ||
    1070                 :             :         ! gnc_numeric_equal(xaccSplitGetAmount(s), xaccSplitGetValue(s))))
    1071                 :             :         {
    1072                 :             :             /* Need to use (or already are using) a list of imbalances in each of
    1073                 :             :                the currencies used in the transaction. */
    1074                 :             :             if (! imbal_list)
    1075                 :             :             {
    1076                 :             :                 /* All previous splits have been in the transaction's common
    1077                 :             :                    currency, so imbal_value is in this currency. */
    1078                 :             :                 imbal_list = gnc_monetary_list_add_value(imbal_list,
    1079                 :             :                 trans->common_currency,
    1080                 :             :                 imbal_value);
    1081                 :             :             }
    1082                 :             :             imbal_list = gnc_monetary_list_add_value(imbal_list, commodity,
    1083                 :             :                          xaccSplitGetAmount(s));
    1084                 :             :         }
    1085                 :             : 
    1086                 :             :         /* Add it to the value accumulator in case we need it. */
    1087                 :             :         imbal_value = gnc_numeric_add(imbal_value, xaccSplitGetValue(s),
    1088                 :             :                                       GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
    1089                 :             :     } );
    1090                 :             : 
    1091                 :             : 
    1092                 :          12 :     if (!imbal_list && !gnc_numeric_zero_p(imbal_value))
    1093                 :             :     {
    1094                 :             :         /* Not balanced and no list, create one.  If we found multiple currencies
    1095                 :             :            and no non-currency commodity then imbal_list will already exist and
    1096                 :             :            we won't get here. */
    1097                 :           1 :         imbal_list = gnc_monetary_list_add_value(imbal_list,
    1098                 :           1 :                      trans->common_currency,
    1099                 :             :                      imbal_value);
    1100                 :             :     }
    1101                 :             : 
    1102                 :             :     /* Delete all the zero entries from the list, perhaps leaving an
    1103                 :             :        empty list */
    1104                 :          12 :     imbal_list = gnc_monetary_list_delete_zeros(imbal_list);
    1105                 :             : 
    1106                 :          12 :     LEAVE("(trans=%p), imbal=%p", trans, imbal_list);
    1107                 :          12 :     return imbal_list;
    1108                 :             : }
    1109                 :             : 
    1110                 :             : gboolean
    1111                 :        3010 : xaccTransIsBalanced (const Transaction *trans)
    1112                 :             : {
    1113                 :             :     MonetaryList *imbal_list;
    1114                 :             :     gboolean result;
    1115                 :        3010 :     gnc_numeric imbal = gnc_numeric_zero();
    1116                 :        3010 :     gnc_numeric imbal_trading = gnc_numeric_zero();
    1117                 :             : 
    1118                 :        3010 :     if (trans == nullptr) return FALSE;
    1119                 :             : 
    1120                 :        3009 :     if (xaccTransUseTradingAccounts(trans))
    1121                 :             :     {
    1122                 :             :         /* Transaction is imbalanced if the value is imbalanced in either
    1123                 :             :            trading or non-trading splits.  One can't be used to balance
    1124                 :             :            the other. */
    1125                 :          30 :         FOR_EACH_SPLIT(trans,
    1126                 :             :         {
    1127                 :             :             Account *acc = xaccSplitGetAccount(s);
    1128                 :             :             if (!acc || xaccAccountGetType(acc) != ACCT_TYPE_TRADING)
    1129                 :             :             {
    1130                 :             :                 imbal = gnc_numeric_add(imbal, xaccSplitGetValue(s),
    1131                 :             :                                         GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
    1132                 :             :             }
    1133                 :             :             else
    1134                 :             :             {
    1135                 :             :                 imbal_trading = gnc_numeric_add(imbal_trading, xaccSplitGetValue(s),
    1136                 :             :                                                 GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
    1137                 :             :             }
    1138                 :             :         }
    1139                 :             :         );
    1140                 :             :     }
    1141                 :             :     else
    1142                 :        3002 :         imbal = xaccTransGetImbalanceValue(trans);
    1143                 :             : 
    1144                 :        3009 :     if (! gnc_numeric_zero_p(imbal) || ! gnc_numeric_zero_p(imbal_trading))
    1145                 :          43 :         return FALSE;
    1146                 :             : 
    1147                 :        2966 :     if (!xaccTransUseTradingAccounts (trans))
    1148                 :        2961 :         return TRUE;
    1149                 :             : 
    1150                 :           5 :     imbal_list = xaccTransGetImbalance(trans);
    1151                 :           5 :     result = imbal_list == nullptr;
    1152                 :           5 :     gnc_monetary_list_free(imbal_list);
    1153                 :           5 :     return result;
    1154                 :             : }
    1155                 :             : 
    1156                 :             : gnc_numeric
    1157                 :           4 : xaccTransGetAccountValue (const Transaction *trans,
    1158                 :             :                           const Account *acc)
    1159                 :             : {
    1160                 :           4 :     gnc_numeric total = gnc_numeric_zero ();
    1161                 :           4 :     if (!trans || !acc) return total;
    1162                 :             : 
    1163                 :           6 :     FOR_EACH_SPLIT(trans, if (acc == xaccSplitGetAccount(s))
    1164                 :             : {
    1165                 :             :     total = gnc_numeric_add (total, xaccSplitGetValue (s),
    1166                 :             :                              GNC_DENOM_AUTO,
    1167                 :             :                              GNC_HOW_DENOM_EXACT);
    1168                 :             :     });
    1169                 :           2 :     return total;
    1170                 :             : }
    1171                 :             : 
    1172                 :             : gnc_numeric
    1173                 :          16 : xaccTransGetAccountAmount (const Transaction *trans, const Account *acc)
    1174                 :             : {
    1175                 :          16 :     gnc_numeric total = gnc_numeric_zero ();
    1176                 :          16 :     if (!trans || !acc) return total;
    1177                 :             : 
    1178                 :          14 :     total = gnc_numeric_convert (total, xaccAccountGetCommoditySCU (acc),
    1179                 :             :                                  GNC_HOW_RND_ROUND_HALF_UP);
    1180                 :          42 :     FOR_EACH_SPLIT(trans, if (acc == xaccSplitGetAccount(s))
    1181                 :             :                    total = gnc_numeric_add_fixed(
    1182                 :             :                                total, xaccSplitGetAmount(s)));
    1183                 :          14 :     return total;
    1184                 :             : }
    1185                 :             : 
    1186                 :             : gnc_numeric
    1187                 :           4 : xaccTransGetAccountConvRate(const Transaction *txn, const Account *acc)
    1188                 :             : {
    1189                 :             :     gnc_numeric amount, value, convrate;
    1190                 :             :     GList *splits;
    1191                 :             :     Split *s;
    1192                 :           4 :     gboolean found_acc_match = FALSE;
    1193                 :           4 :     gnc_commodity *acc_commod = xaccAccountGetCommodity(acc);
    1194                 :             : 
    1195                 :             :     /* We need to compute the conversion rate into _this account_.  So,
    1196                 :             :      * find the first split into this account, compute the conversion
    1197                 :             :      * rate (based on amount/value), and then return this conversion
    1198                 :             :      * rate.
    1199                 :             :      */
    1200                 :           4 :     if (gnc_commodity_equal(acc_commod, xaccTransGetCurrency(txn)))
    1201                 :           1 :         return gnc_numeric_create(1, 1);
    1202                 :             : 
    1203                 :           4 :     for (splits = txn->splits; splits; splits = splits->next)
    1204                 :             :     {
    1205                 :             :         Account *split_acc;
    1206                 :             :         gnc_commodity *split_commod;
    1207                 :             : 
    1208                 :           4 :         s = GNC_SPLIT(splits->data);
    1209                 :             : 
    1210                 :           4 :         if (!xaccTransStillHasSplit(txn, s))
    1211                 :           0 :             continue;
    1212                 :           4 :         split_acc = xaccSplitGetAccount (s);
    1213                 :           4 :         split_commod = xaccAccountGetCommodity (split_acc);
    1214                 :           5 :         if (! (split_acc == acc ||
    1215                 :           1 :                 gnc_commodity_equal (split_commod, acc_commod)))
    1216                 :           1 :             continue;
    1217                 :             : 
    1218                 :           3 :         found_acc_match = TRUE;
    1219                 :           3 :         amount = xaccSplitGetAmount (s);
    1220                 :             : 
    1221                 :             :         /* Ignore splits with "zero" amount */
    1222                 :           3 :         if (gnc_numeric_zero_p (amount))
    1223                 :           0 :             continue;
    1224                 :             : 
    1225                 :           3 :         value = xaccSplitGetValue (s);
    1226                 :           3 :         if (gnc_numeric_zero_p (value))
    1227                 :           1 :             PWARN("How can amount be nonzero and value be zero?");
    1228                 :             : 
    1229                 :           3 :         convrate = gnc_numeric_div(amount, value, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE);
    1230                 :           3 :         return convrate;
    1231                 :             :     }
    1232                 :             : 
    1233                 :           0 :     if (acc)
    1234                 :             :     {
    1235                 :             :         /* If we did find a matching account but its amount was zero,
    1236                 :             :          * then perhaps this is a "special" income/loss transaction
    1237                 :             :          */
    1238                 :           0 :         if (found_acc_match)
    1239                 :           0 :             return gnc_numeric_zero();
    1240                 :             :         else
    1241                 :           0 :             PERR("Cannot convert transaction -- no splits with proper conversion ratio");
    1242                 :             :     }
    1243                 :           0 :     return gnc_numeric_create (100, 100);
    1244                 :             : }
    1245                 :             : 
    1246                 :             : gnc_numeric
    1247                 :           4 : xaccTransGetAccountBalance (const Transaction *trans,
    1248                 :             :                             const Account *account)
    1249                 :             : {
    1250                 :             :     GList *node;
    1251                 :           4 :     Split *last_split = nullptr;
    1252                 :             : 
    1253                 :             :     // Not really the appropriate error value.
    1254                 :           4 :     g_return_val_if_fail(account && trans, gnc_numeric_error(GNC_ERROR_ARG));
    1255                 :             : 
    1256                 :           6 :     for (node = trans->splits; node; node = node->next)
    1257                 :             :     {
    1258                 :           4 :         Split *split = GNC_SPLIT(node->data);
    1259                 :             : 
    1260                 :           4 :         if (!xaccTransStillHasSplit(trans, split))
    1261                 :           0 :             continue;
    1262                 :           4 :         if (xaccSplitGetAccount(split) != account)
    1263                 :           2 :             continue;
    1264                 :             : 
    1265                 :           2 :         if (!last_split)
    1266                 :             :         {
    1267                 :           2 :             last_split = split;
    1268                 :           2 :             continue;
    1269                 :             :         }
    1270                 :             : 
    1271                 :             :         /* This test needs to correspond to the comparison function used when
    1272                 :             :            sorting the splits for computing the running balance. */
    1273                 :           0 :         if (xaccSplitOrder (last_split, split) < 0)
    1274                 :           0 :             last_split = split;
    1275                 :             :     }
    1276                 :             : 
    1277                 :           2 :     return xaccSplitGetBalance (last_split);
    1278                 :             : }
    1279                 :             : 
    1280                 :             : /********************************************************************\
    1281                 :             : \********************************************************************/
    1282                 :             : /* The new routine for setting the common currency */
    1283                 :             : 
    1284                 :             : gnc_commodity *
    1285                 :      199578 : xaccTransGetCurrency (const Transaction *trans)
    1286                 :             : {
    1287                 :      199578 :     return trans ? trans->common_currency : nullptr;
    1288                 :             : }
    1289                 :             : 
    1290                 :             : /* Helper functions for xaccTransSetCurrency */
    1291                 :             : static gnc_numeric
    1292                 :         201 : find_new_rate(Transaction *trans, gnc_commodity *curr)
    1293                 :             : {
    1294                 :             :     GList *node;
    1295                 :         201 :     gnc_numeric rate = gnc_numeric_zero();
    1296                 :         524 :     for (node = trans->splits; node != nullptr; node = g_list_next (node))
    1297                 :             :     {
    1298                 :         389 :         Split *split = GNC_SPLIT(node->data);
    1299                 :             :         gnc_commodity *split_com =
    1300                 :         389 :             xaccAccountGetCommodity(xaccSplitGetAccount(split));
    1301                 :         389 :         if (gnc_commodity_equal(curr, split_com))
    1302                 :             :         {
    1303                 :             : /* This looks backwards, but the amount of the balancing transaction
    1304                 :             :  * that we're going to use it on is in the value's currency. */
    1305                 :          66 :             rate = gnc_numeric_div(xaccSplitGetAmount(split),
    1306                 :             :                                    xaccSplitGetValue(split),
    1307                 :             :                                    GNC_DENOM_AUTO, GNC_HOW_RND_NEVER);
    1308                 :          66 :             break;
    1309                 :             :         }
    1310                 :             :     }
    1311                 :         201 :     return rate;
    1312                 :             : }
    1313                 :             : 
    1314                 :             : static void
    1315                 :         130 : split_set_new_value(Split* split, gnc_commodity *curr, gnc_commodity *old_curr,
    1316                 :             :                     gnc_numeric rate)
    1317                 :             : {
    1318                 :             :     gnc_commodity *split_com =
    1319                 :         130 :         xaccAccountGetCommodity(xaccSplitGetAccount(split));
    1320                 :         130 :     if (gnc_commodity_equal(curr, split_com))
    1321                 :          67 :         xaccSplitSetValue(split, xaccSplitGetAmount(split));
    1322                 :          63 :     else if (gnc_commodity_equal(old_curr, split_com))
    1323                 :          63 :         xaccSplitSetSharePrice(split, rate);
    1324                 :             :     else
    1325                 :             :     {
    1326                 :           0 :         gnc_numeric old_rate = gnc_numeric_div(xaccSplitGetValue(split),
    1327                 :             :                                                xaccSplitGetAmount(split),
    1328                 :             :                                                GNC_DENOM_AUTO,
    1329                 :             :                                                GNC_HOW_RND_NEVER);
    1330                 :           0 :         gnc_numeric new_rate = gnc_numeric_div(old_rate, rate, GNC_DENOM_AUTO,
    1331                 :             :                                                GNC_HOW_RND_NEVER);
    1332                 :           0 :         xaccSplitSetSharePrice(split, new_rate);
    1333                 :             :     }
    1334                 :         130 : }
    1335                 :             : 
    1336                 :             : /**
    1337                 :             :  * Set a new currency on a transaction.
    1338                 :             :  * When we do that to a transaction with splits we need to re-value
    1339                 :             :  * all of the splits in the new currency.
    1340                 :             :  * @param trans: The transaction to change
    1341                 :             :  * @param curr: The new currency to set.
    1342                 :             :  */
    1343                 :             : void
    1344                 :        3590 : xaccTransSetCurrency (Transaction *trans, gnc_commodity *curr)
    1345                 :             : {
    1346                 :        3590 :     gnc_commodity *old_curr = trans->common_currency;
    1347                 :        3590 :     if (!trans || !curr || trans->common_currency == curr) return;
    1348                 :        3392 :     xaccTransBeginEdit(trans);
    1349                 :             : 
    1350                 :        3392 :     trans->common_currency = curr;
    1351                 :        3392 :     if (old_curr != nullptr && trans->splits != nullptr)
    1352                 :             :     {
    1353                 :         201 :         gnc_numeric rate = find_new_rate(trans, curr);
    1354                 :         201 :         if (!gnc_numeric_zero_p (rate))
    1355                 :             :         {
    1356                 :         193 :             FOR_EACH_SPLIT(trans, split_set_new_value(s, curr, old_curr, rate));
    1357                 :             :         }
    1358                 :             :         else
    1359                 :             :         {
    1360                 :         417 :             FOR_EACH_SPLIT(trans, xaccSplitSetValue(s, xaccSplitGetValue(s)));
    1361                 :             :         }
    1362                 :             :     }
    1363                 :             : 
    1364                 :        3392 :     qof_instance_set_dirty(QOF_INSTANCE(trans));
    1365                 :        3392 :     mark_trans(trans);  /* Dirty balance of every account in trans */
    1366                 :        3392 :     xaccTransCommitEdit(trans);
    1367                 :             : }
    1368                 :             : 
    1369                 :             : /********************************************************************\
    1370                 :             : \********************************************************************/
    1371                 :             : 
    1372                 :             : void
    1373                 :       82056 : xaccTransBeginEdit (Transaction *trans)
    1374                 :             : {
    1375                 :       82056 :     if (!trans) return;
    1376                 :       67930 :     if (!qof_begin_edit(&trans->inst)) return;
    1377                 :             : 
    1378                 :        7052 :     if (qof_book_shutting_down(qof_instance_get_book(trans))) return;
    1379                 :             : 
    1380                 :        4797 :     if (!qof_book_is_readonly(qof_instance_get_book(trans)))
    1381                 :             :     {
    1382                 :        4794 :         xaccOpenLog ();
    1383                 :        4794 :         xaccTransWriteLog (trans, 'B');
    1384                 :             :     }
    1385                 :             : 
    1386                 :             :     /* Make a clone of the transaction; we will use this
    1387                 :             :      * in case we need to roll-back the edit. */
    1388                 :        4797 :     trans->orig = dupe_trans (trans);
    1389                 :             : }
    1390                 :             : 
    1391                 :             : /********************************************************************\
    1392                 :             : \********************************************************************/
    1393                 :             : 
    1394                 :             : void
    1395                 :        2384 : xaccTransDestroy (Transaction *trans)
    1396                 :             : {
    1397                 :        2384 :     if (!trans) return;
    1398                 :             : 
    1399                 :        2485 :     if (!xaccTransGetReadOnly (trans) ||
    1400                 :         101 :             qof_book_shutting_down(qof_instance_get_book(trans)))
    1401                 :             :     {
    1402                 :        2370 :         xaccTransBeginEdit(trans);
    1403                 :        2370 :         qof_instance_set_destroying(trans, TRUE);
    1404                 :        2370 :         xaccTransCommitEdit(trans);
    1405                 :             :     }
    1406                 :             : }
    1407                 :             : 
    1408                 :             : static void
    1409                 :         116 : destroy_gains (Transaction *trans)
    1410                 :             : {
    1411                 :             :     SplitList *node;
    1412                 :         342 :     for (node = trans->splits; node; node = node->next)
    1413                 :             :     {
    1414                 :         226 :         Split *s = GNC_SPLIT(node->data);
    1415                 :         226 :         if (!xaccTransStillHasSplit(trans, s))
    1416                 :           1 :             continue;
    1417                 :             : 
    1418                 :         225 :         if (GAINS_STATUS_UNKNOWN == s->gains) xaccSplitDetermineGainStatus(s);
    1419                 :         225 :         if (s->gains_split && (GAINS_STATUS_GAINS & s->gains_split->gains))
    1420                 :             :         {
    1421                 :           2 :             Transaction *t = s->gains_split->parent;
    1422                 :           2 :             xaccTransDestroy (t);
    1423                 :           2 :             s->gains_split = nullptr;
    1424                 :             :         }
    1425                 :             :     }
    1426                 :         116 : }
    1427                 :             : 
    1428                 :             : static void
    1429                 :        2370 : do_destroy (QofInstance* inst)
    1430                 :             : {
    1431                 :        2370 :     Transaction *trans{GNC_TRANSACTION (inst)};
    1432                 :        2370 :     gboolean shutting_down = qof_book_shutting_down(qof_instance_get_book(trans));
    1433                 :             : 
    1434                 :             :     /* If there are capital-gains transactions associated with this,
    1435                 :             :      * they need to be destroyed too unless we're shutting down in
    1436                 :             :      * which case all transactions will be destroyed. */
    1437                 :        2370 :     if (!shutting_down)
    1438                 :         115 :         destroy_gains (trans);
    1439                 :             : 
    1440                 :             :     /* Make a log in the journal before destruction.  */
    1441                 :        2370 :     if (!shutting_down && !qof_book_is_readonly(qof_instance_get_book(trans)))
    1442                 :         114 :         xaccTransWriteLog (trans, 'D');
    1443                 :             : 
    1444                 :        2370 :     qof_event_gen (&trans->inst, QOF_EVENT_DESTROY, nullptr);
    1445                 :             :     /* xaccFreeTransaction will also clean up the splits but without
    1446                 :             :      * emitting GNC_EVENT_ITEM_REMOVED.
    1447                 :             :      */
    1448                 :        2370 :     xaccTransClearSplits(trans);
    1449                 :        2370 :     xaccFreeTransaction (trans);
    1450                 :        2370 : }
    1451                 :             : 
    1452                 :             : /********************************************************************\
    1453                 :             : \********************************************************************/
    1454                 :             : 
    1455                 :             : /* Temporary hack for data consistency */
    1456                 :             : static int scrub_data = 1;
    1457                 :          28 : void xaccEnableDataScrubbing(void)
    1458                 :             : {
    1459                 :          28 :     scrub_data = 1;
    1460                 :          28 : }
    1461                 :          28 : void xaccDisableDataScrubbing(void)
    1462                 :             : {
    1463                 :          28 :     scrub_data = 0;
    1464                 :          28 : }
    1465                 :             : 
    1466                 :             : /* Check for an implicitly deleted transaction */
    1467                 :        6934 : static gboolean was_trans_emptied(Transaction *trans)
    1468                 :             : {
    1469                 :        6938 :     FOR_EACH_SPLIT(trans, return FALSE);
    1470                 :           5 :     return TRUE;
    1471                 :             : }
    1472                 :             : 
    1473                 :           1 : static void trans_on_error(QofInstance *inst, QofBackendError errcode)
    1474                 :             : {
    1475                 :           1 :     Transaction *trans{GNC_TRANSACTION(inst)};
    1476                 :             : 
    1477                 :             :     /* If the backend puked, then we must roll-back
    1478                 :             :      * at this point, and let the user know that we failed.
    1479                 :             :      * The GUI should check for error conditions ...
    1480                 :             :      */
    1481                 :           1 :     if (ERR_BACKEND_MODIFIED == errcode)
    1482                 :             :     {
    1483                 :           1 :         PWARN("Another user has modified this transaction\n"
    1484                 :             :               "\tjust a moment ago. Please look at their changes,\n"
    1485                 :             :               "\tand try again, if needed.\n");
    1486                 :             :     }
    1487                 :             : 
    1488                 :           1 :     xaccTransRollbackEdit(trans);
    1489                 :           1 :     gnc_engine_signal_commit_error( errcode );
    1490                 :           1 : }
    1491                 :             : 
    1492                 :        4566 : static void trans_cleanup_commit(QofInstance *inst)
    1493                 :             : {
    1494                 :        4566 :     Transaction *trans{GNC_TRANSACTION(inst)};
    1495                 :             :     GList *slist, *node;
    1496                 :             : 
    1497                 :             :     /* ------------------------------------------------- */
    1498                 :             :     /* Make sure all associated splits are in proper order
    1499                 :             :      * in their accounts with the correct balances. */
    1500                 :             : 
    1501                 :             :     /* Iterate over existing splits */
    1502                 :        4566 :     slist = g_list_copy(trans->splits);
    1503                 :       13932 :     for (node = slist; node; node = node->next)
    1504                 :             :     {
    1505                 :        9366 :         Split *s = GNC_SPLIT(node->data);
    1506                 :        9366 :         if (!qof_instance_is_dirty(QOF_INSTANCE(s)))
    1507                 :        1310 :             continue;
    1508                 :             : 
    1509                 :        8056 :         if ((s->parent != trans) || qof_instance_get_destroying(s))
    1510                 :             :         {
    1511                 :             :             /* Existing split either moved to another transaction or
    1512                 :             :                was destroyed, drop from list */
    1513                 :             :             GncEventData ed;
    1514                 :          11 :             ed.node = trans;
    1515                 :          11 :             ed.idx = g_list_index(trans->splits, s);
    1516                 :          11 :             trans->splits = g_list_remove(trans->splits, s);
    1517                 :          11 :             qof_event_gen(&s->inst, QOF_EVENT_REMOVE, &ed);
    1518                 :             :         }
    1519                 :             : 
    1520                 :        8056 :         if (s->parent == trans)
    1521                 :             :         {
    1522                 :             :             /* Split was either added, destroyed or just changed */
    1523                 :        8050 :             if (qof_instance_get_destroying(s))
    1524                 :           5 :                 qof_event_gen(&s->inst, QOF_EVENT_DESTROY, nullptr);
    1525                 :        8045 :             else qof_event_gen(&s->inst, QOF_EVENT_MODIFY, nullptr);
    1526                 :        8050 :             xaccSplitCommitEdit(s);
    1527                 :             :         }
    1528                 :             :     }
    1529                 :        4566 :     g_list_free(slist);
    1530                 :             : 
    1531                 :        4566 :     if (!qof_book_is_readonly(qof_instance_get_book(trans)))
    1532                 :        4566 :         xaccTransWriteLog (trans, 'C');
    1533                 :             : 
    1534                 :             :     /* Get rid of the copy we made. We won't be rolling back,
    1535                 :             :      * so we don't need it any more.  */
    1536                 :        4566 :     PINFO ("get rid of rollback trans=%p", trans->orig);
    1537                 :        4566 :     xaccFreeTransaction (trans->orig);
    1538                 :        4566 :     trans->orig = nullptr;
    1539                 :             : 
    1540                 :             :     /* Sort the splits. Why do we need to do this ?? */
    1541                 :             :     /* Good question.  Who knows?  */
    1542                 :        4566 :     xaccTransSortSplits(trans);
    1543                 :             : 
    1544                 :             :     /* Put back to zero. */
    1545                 :        4566 :     qof_instance_decrease_editlevel(trans);
    1546                 :        4566 :     g_assert(qof_instance_get_editlevel(trans) == 0);
    1547                 :             : 
    1548                 :        4566 :     gen_event_trans (trans); //TODO: could be conditional
    1549                 :        4566 :     qof_event_gen (&trans->inst, QOF_EVENT_MODIFY, nullptr);
    1550                 :        4566 : }
    1551                 :             : 
    1552                 :             : void
    1553                 :       81935 : xaccTransCommitEdit (Transaction *trans)
    1554                 :             : {
    1555                 :       81935 :     if (!trans) return;
    1556                 :       67809 :     ENTER ("(trans=%p)", trans);
    1557                 :             : 
    1558                 :       67809 :     if (!qof_commit_edit (QOF_INSTANCE(trans)))
    1559                 :             :     {
    1560                 :       60877 :         LEAVE("editlevel non-zero");
    1561                 :       60877 :         return;
    1562                 :             :     }
    1563                 :             : 
    1564                 :             :     /* We increment this for the duration of the call
    1565                 :             :      * so other functions don't result in a recursive
    1566                 :             :      * call to xaccTransCommitEdit. */
    1567                 :        6932 :     qof_instance_increase_editlevel(trans);
    1568                 :             : 
    1569                 :        6932 :     if (was_trans_emptied(trans))
    1570                 :           4 :         qof_instance_set_destroying(trans, TRUE);
    1571                 :             : 
    1572                 :             :     /* Before committing the transaction, we are going to enforce certain
    1573                 :             :      * constraints.  In particular, we want to enforce the cap-gains
    1574                 :             :      * and the balanced lot constraints.  These constraints might
    1575                 :             :      * change the number of splits in this transaction, and the
    1576                 :             :      * transaction itself might be deleted.  This is also why
    1577                 :             :      * we can't really enforce these constraints elsewhere: they
    1578                 :             :      * can cause pointers to splits and transactions to disappear out
    1579                 :             :      * from under the holder.
    1580                 :             :      */
    1581                 :        9929 :     if (!qof_instance_get_destroying(trans) && scrub_data &&
    1582                 :        2997 :             !qof_book_shutting_down(xaccTransGetBook(trans)))
    1583                 :             :     {
    1584                 :             :         /* If scrubbing gains recurses through here, don't call it again. */
    1585                 :        2997 :         scrub_data = 0;
    1586                 :             :         /* The total value of the transaction should sum to zero.
    1587                 :             :          * Call the trans scrub routine to fix it. Indirectly, this
    1588                 :             :          * routine also performs a number of other transaction fixes too.
    1589                 :             :          */
    1590                 :        2997 :         xaccTransScrubImbalance (trans, nullptr, nullptr);
    1591                 :             :         /* Get the cap gains into a consistent state as well. */
    1592                 :             : 
    1593                 :             :         /* Lot Scrubbing is temporarily disabled. */
    1594                 :        2997 :         if (g_getenv("GNC_AUTO_SCRUB_LOTS") != nullptr)
    1595                 :           0 :             xaccTransScrubGains (trans, nullptr);
    1596                 :             : 
    1597                 :             :         /* Allow scrubbing in transaction commit again */
    1598                 :        2997 :         scrub_data = 1;
    1599                 :             :     }
    1600                 :             : 
    1601                 :             :     /* Record the time of last modification */
    1602                 :        6932 :     if (0 == trans->date_entered)
    1603                 :             :     {
    1604                 :        2078 :         trans->date_entered = gnc_time(nullptr);
    1605                 :        2078 :         qof_instance_set_dirty(QOF_INSTANCE(trans));
    1606                 :             :     }
    1607                 :             : 
    1608                 :        6932 :     trans->txn_type = TXN_TYPE_UNCACHED;
    1609                 :        6932 :     qof_commit_edit_part2(QOF_INSTANCE(trans), trans_on_error,
    1610                 :             :                           trans_cleanup_commit, do_destroy);
    1611                 :        6932 :     LEAVE ("(trans=%p)", trans);
    1612                 :             : }
    1613                 :             : 
    1614                 :             : /* Ughhh. The Rollback function is terribly complex, and, what's worse,
    1615                 :             :  * it only rolls back the basics.  The TransCommit functions did a bunch
    1616                 :             :  * of Lot/Cap-gains scrubbing that don't get addressed/undone here, and
    1617                 :             :  * so the rollback can potentially leave a bit of a mess behind.  We
    1618                 :             :  * really need a more robust undo capability.  Part of the problem is
    1619                 :             :  * that the biggest user of the undo is the multi-user backend, which
    1620                 :             :  * also adds complexity.
    1621                 :             :  */
    1622                 :             : void
    1623                 :          16 : xaccTransRollbackEdit (Transaction *trans)
    1624                 :             : {
    1625                 :             :     GList *node, *onode;
    1626                 :             :     QofBackend *be;
    1627                 :             :     Transaction *orig;
    1628                 :             :     GList *slist;
    1629                 :             :     int num_preexist, i;
    1630                 :             : 
    1631                 :             : /* FIXME: This isn't quite the right way to handle nested edits --
    1632                 :             :  * there should be a stack of transaction states that are popped off
    1633                 :             :  * and restored at each level -- but it does prevent restoring to the
    1634                 :             :  * editlevel 0 state until one is returning to editlevel 0, and
    1635                 :             :  * thereby prevents a crash caused by trans->orig getting nullptred too
    1636                 :             :  * soon.
    1637                 :             :  */
    1638                 :          16 :     if (!qof_instance_get_editlevel (QOF_INSTANCE (trans))) return;
    1639                 :          15 :     if (qof_instance_get_editlevel (QOF_INSTANCE (trans)) > 1) {
    1640                 :           2 :          qof_instance_decrease_editlevel (QOF_INSTANCE (trans));
    1641                 :           2 :          return;
    1642                 :             :     }
    1643                 :             : 
    1644                 :          13 :     ENTER ("trans addr=%p\n", trans);
    1645                 :             : 
    1646                 :          13 :     check_open(trans);
    1647                 :             : 
    1648                 :             :     /* copy the original values back in. */
    1649                 :             : 
    1650                 :          13 :     orig = trans->orig;
    1651                 :          13 :     std::swap (trans->num, orig->num);
    1652                 :          13 :     std::swap (trans->description, orig->description);
    1653                 :          13 :     trans->date_entered = orig->date_entered;
    1654                 :          13 :     trans->date_posted = orig->date_posted;
    1655                 :          13 :     std::swap (trans->common_currency, orig->common_currency);
    1656                 :          13 :     qof_instance_swap_kvp (QOF_INSTANCE (trans), QOF_INSTANCE (orig));
    1657                 :             : 
    1658                 :             :     /* The splits at the front of trans->splits are exactly the same
    1659                 :             :        splits as in the original, but some of them may have changed, so
    1660                 :             :        we restore only those. */
    1661                 :             : /* FIXME: Runs off the transaction's splits, so deleted splits are not
    1662                 :             :  * restored!
    1663                 :             :  */
    1664                 :          13 :     num_preexist = g_list_length(orig->splits);
    1665                 :          13 :     slist = g_list_copy(trans->splits);
    1666                 :          34 :     for (i = 0, node = slist, onode = orig->splits; node;
    1667                 :          21 :             i++, node = node->next, onode = onode ? onode->next : nullptr)
    1668                 :             :     {
    1669                 :          21 :         Split *s = GNC_SPLIT(node->data);
    1670                 :             : 
    1671                 :          21 :         if (!qof_instance_is_dirty(QOF_INSTANCE(s)))
    1672                 :          16 :             continue;
    1673                 :             : 
    1674                 :           5 :         if (i < num_preexist && onode)
    1675                 :             :         {
    1676                 :           2 :             Split *so = GNC_SPLIT(onode->data);
    1677                 :             : 
    1678                 :           2 :             xaccSplitRollbackEdit(s);
    1679                 :           2 :             std::swap (s->action, so->action);
    1680                 :           2 :             std::swap (s->memo, so->memo);
    1681                 :           2 :             qof_instance_copy_kvp (QOF_INSTANCE (s), QOF_INSTANCE (so));
    1682                 :           2 :             s->reconciled = so->reconciled;
    1683                 :           2 :             s->amount = so->amount;
    1684                 :           2 :             s->value = so->value;
    1685                 :           2 :             s->lot = so->lot;
    1686                 :           2 :             s->gains_split = so->gains_split;
    1687                 :             :             //SET_GAINS_A_VDIRTY(s);
    1688                 :           2 :             s->date_reconciled = so->date_reconciled;
    1689                 :           2 :             qof_instance_mark_clean(QOF_INSTANCE(s));
    1690                 :           2 :         }
    1691                 :             :         else
    1692                 :             :         {
    1693                 :             :             /* Potentially added splits */
    1694                 :           3 :             if (trans != xaccSplitGetParent(s))
    1695                 :             :             {
    1696                 :           0 :                 trans->splits = g_list_remove(trans->splits, s);
    1697                 :             :                 /* New split added, but then moved to another
    1698                 :             :                    transaction */
    1699                 :           0 :                 continue;
    1700                 :             :             }
    1701                 :           3 :             xaccSplitRollbackEdit(s);
    1702                 :           3 :             trans->splits = g_list_remove(trans->splits, s);
    1703                 :           3 :             g_assert(trans != xaccSplitGetParent(s));
    1704                 :             :             /* NB: our memory management policy here is that a new split
    1705                 :             :                added to the transaction which is then rolled-back still
    1706                 :             :                belongs to the engine.  Specifically, it's freed by the
    1707                 :             :                transaction to which it was added.  Don't add the Split to
    1708                 :             :                more than one transaction during the begin/commit block! */
    1709                 :           3 :             if (nullptr == xaccSplitGetParent(s))
    1710                 :             :             {
    1711                 :           3 :                 xaccFreeSplit(s);  // a newly malloc'd split
    1712                 :             :             }
    1713                 :             :         }
    1714                 :             :     }
    1715                 :          13 :     g_list_free(slist);
    1716                 :             : 
    1717                 :             :     // orig->splits may still have duped splits so free them
    1718                 :          13 :     g_list_free_full (orig->splits, (GDestroyNotify)xaccFreeSplit);
    1719                 :          13 :     orig->splits = nullptr;
    1720                 :             : 
    1721                 :             :     /* Now that the engine copy is back to its original version,
    1722                 :             :      * get the backend to fix it in the database */
    1723                 :          13 :     be = qof_book_get_backend(qof_instance_get_book(trans));
    1724                 :             :     /** \todo Fix transrollbackedit in QOF so that rollback
    1725                 :             :     is exposed via the API. */
    1726                 :          13 :     if (qof_backend_can_rollback (be))
    1727                 :             :     {
    1728                 :             :         QofBackendError errcode;
    1729                 :             : 
    1730                 :             :         /* clear errors */
    1731                 :             :         do
    1732                 :             :         {
    1733                 :           9 :             errcode = qof_backend_get_error (be);
    1734                 :             :         }
    1735                 :           9 :         while (ERR_BACKEND_NO_ERR != errcode);
    1736                 :             : 
    1737                 :           8 :         qof_backend_rollback_instance (be, &(trans->inst));
    1738                 :             : 
    1739                 :           8 :         errcode = qof_backend_get_error (be);
    1740                 :           8 :         if (ERR_BACKEND_MOD_DESTROY == errcode)
    1741                 :             :         {
    1742                 :             :             /* The backend is asking us to delete this transaction.
    1743                 :             :              * This typically happens because another (remote) user
    1744                 :             :              * has deleted this transaction, and we haven't found
    1745                 :             :              * out about it until this user tried to edit it.
    1746                 :             :              */
    1747                 :           1 :             xaccTransDestroy (trans);
    1748                 :           1 :             do_destroy (QOF_INSTANCE(trans));
    1749                 :             : 
    1750                 :             :             /* push error back onto the stack */
    1751                 :           1 :             qof_backend_set_error (be, errcode);
    1752                 :           1 :             LEAVE ("deleted trans addr=%p\n", trans);
    1753                 :           1 :             return;
    1754                 :             :         }
    1755                 :           7 :         if (ERR_BACKEND_NO_ERR != errcode)
    1756                 :             :         {
    1757                 :           1 :             PERR ("Rollback Failed.  Ouch!");
    1758                 :             :             /* push error back onto the stack */
    1759                 :           1 :             qof_backend_set_error (be, errcode);
    1760                 :             :         }
    1761                 :             :     }
    1762                 :             : 
    1763                 :          12 :     if (!qof_book_is_readonly(qof_instance_get_book(trans)))
    1764                 :          10 :         xaccTransWriteLog (trans, 'R');
    1765                 :             : 
    1766                 :          12 :     xaccFreeTransaction (trans->orig);
    1767                 :             : 
    1768                 :          12 :     trans->orig = nullptr;
    1769                 :          12 :     qof_instance_set_destroying(trans, FALSE);
    1770                 :             : 
    1771                 :             :     /* Put back to zero. */
    1772                 :          12 :     qof_instance_decrease_editlevel(trans);
    1773                 :             :     /* FIXME: The register code seems to depend on the engine to
    1774                 :             :        generate an event during rollback, even though the state is just
    1775                 :             :        reverting to what it was. */
    1776                 :          12 :     gen_event_trans (trans);
    1777                 :             : 
    1778                 :          12 :     LEAVE ("trans addr=%p\n", trans);
    1779                 :             : }
    1780                 :             : 
    1781                 :             : gboolean
    1782                 :          74 : xaccTransIsOpen (const Transaction *trans)
    1783                 :             : {
    1784                 :          74 :     return trans ? (0 < qof_instance_get_editlevel(trans)) : FALSE;
    1785                 :             : }
    1786                 :             : 
    1787                 :             : #define SECS_PER_DAY 86400
    1788                 :             : 
    1789                 :             : int
    1790                 :      730428 : xaccTransOrder (const Transaction *ta, const Transaction *tb)
    1791                 :             : {
    1792                 :      730428 :     return xaccTransOrder_num_action (ta, nullptr, tb, nullptr);
    1793                 :             : }
    1794                 :             : 
    1795                 :             : /* Order a pair of potentially numeric string as numbers if both
    1796                 :             :  * strings begin with numbers, ordering the remainder of the string
    1797                 :             :  * lexically if the numeric parts are equal, and the whole strings
    1798                 :             :  * lexically otherwise.
    1799                 :             :  *
    1800                 :             :  * Note that this won't work well for numbers > 10^18 and that
    1801                 :             :  * negative numbers are treated as strings and will cause the pair to
    1802                 :             :  * be ordered lexically.
    1803                 :             :  */
    1804                 :             : 
    1805                 :             : static int
    1806                 :       61328 : order_by_int64_or_string (const char* a, const char* b)
    1807                 :             : {
    1808                 :       61328 :      char *end_a = nullptr, *end_b = nullptr;
    1809                 :       61328 :      int cmp = 0;
    1810                 :       61328 :      uint64_t na = strtoull(a, &end_a, 10);
    1811                 :       61328 :      uint64_t nb = strtoull(b, &end_b, 10);
    1812                 :       61328 :      if (na && nb)
    1813                 :             :      {
    1814                 :         609 :           if (na != nb)
    1815                 :         475 :                return na < nb ? -1 : 1;
    1816                 :         134 :           cmp = g_utf8_collate(end_a, end_b);
    1817                 :             :      }
    1818                 :             :      else
    1819                 :             :      {
    1820                 :       60719 :           cmp = g_utf8_collate(a, b);
    1821                 :             :      }
    1822                 :       60853 :      return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
    1823                 :             : }
    1824                 :             : 
    1825                 :             : int
    1826                 :      730443 : xaccTransOrder_num_action (const Transaction *ta, const char *actna,
    1827                 :             :                             const Transaction *tb, const char *actnb)
    1828                 :             : {
    1829                 :             :     const char *da, *db;
    1830                 :             :     int retval;
    1831                 :             : 
    1832                 :      730443 :     if ( ta && !tb ) return -1;
    1833                 :      730441 :     if ( !ta && tb ) return +1;
    1834                 :      730440 :     if ( !ta && !tb ) return 0;
    1835                 :             : 
    1836                 :      730314 :     if (ta->date_posted != tb->date_posted)
    1837                 :      668937 :         return (ta->date_posted > tb->date_posted) - (ta->date_posted < tb->date_posted);
    1838                 :             : 
    1839                 :             :     /* Always sort closing transactions after normal transactions */
    1840                 :             :     {
    1841                 :       61377 :         gboolean ta_is_closing = xaccTransGetIsClosingTxn (ta);
    1842                 :       61377 :         gboolean tb_is_closing = xaccTransGetIsClosingTxn (tb);
    1843                 :       61377 :         if (ta_is_closing != tb_is_closing)
    1844                 :          49 :             return (ta_is_closing - tb_is_closing);
    1845                 :             :     }
    1846                 :             : 
    1847                 :             :     /* otherwise, sort on number string */
    1848                 :       61328 :     if (actna && actnb) /* split action string, if not nullptr */
    1849                 :             :     {
    1850                 :           3 :          retval = order_by_int64_or_string (actna, actnb);
    1851                 :             :     }
    1852                 :             :     else                /* else transaction num string */
    1853                 :             :     {
    1854                 :       61325 :          retval = order_by_int64_or_string (ta->num, tb->num);
    1855                 :             :     }
    1856                 :       61328 :     if (retval)
    1857                 :        3159 :          return retval;
    1858                 :             : 
    1859                 :       58169 :     if (ta->date_entered != tb->date_entered)
    1860                 :          70 :         return (ta->date_entered > tb->date_entered) - (ta->date_entered < tb->date_entered);
    1861                 :             : 
    1862                 :             :     /* otherwise, sort on description string */
    1863                 :       58099 :     da = ta->description ? ta->description : "";
    1864                 :       58099 :     db = tb->description ? tb->description : "";
    1865                 :       58099 :     retval = g_utf8_collate (da, db);
    1866                 :       58099 :     if (retval)
    1867                 :       46953 :         return retval;
    1868                 :             : 
    1869                 :             :     /* else, sort on guid - keeps sort stable. */
    1870                 :       11146 :     return qof_instance_guid_compare(ta, tb);
    1871                 :             : }
    1872                 :             : 
    1873                 :             : /********************************************************************\
    1874                 :             : \********************************************************************/
    1875                 :             : 
    1876                 :             : static void
    1877                 :         689 : set_kvp_string_path (Transaction *txn, const Path& path, const char *value)
    1878                 :             : {
    1879                 :         689 :     g_return_if_fail (GNC_IS_TRANSACTION(txn));
    1880                 :         689 :     xaccTransBeginEdit(txn);
    1881                 :        1348 :     auto val = value && *value ? std::make_optional<const char*>(g_strdup(value)) : std::nullopt;
    1882                 :         689 :     qof_instance_set_path_kvp<const char*> (QOF_INSTANCE(txn), val, path);
    1883                 :         689 :     qof_instance_set_dirty (QOF_INSTANCE(txn));
    1884                 :         689 :     xaccTransCommitEdit(txn);
    1885                 :             : }
    1886                 :             : 
    1887                 :             : static const char*
    1888                 :       14208 : get_kvp_string_path (const Transaction *txn, const Path& path)
    1889                 :             : {
    1890                 :       14208 :     auto rv{qof_instance_get_path_kvp<const char*> (QOF_INSTANCE(txn), path)};
    1891                 :       28416 :     return rv ? *rv : nullptr;
    1892                 :             : }
    1893                 :             : 
    1894                 :             : static inline void
    1895                 :        4554 : xaccTransSetDateInternal(Transaction *trans, time64 *dadate, time64 val)
    1896                 :             : {
    1897                 :        4554 :     xaccTransBeginEdit(trans);
    1898                 :             : 
    1899                 :             : #if 0 /* gnc_ctime is expensive so change to 1 only if you need to debug setting
    1900                 :             :        * dates. */
    1901                 :             :    {
    1902                 :             :         time64 secs = (time64) val.tv_sec;
    1903                 :             :         gchar *tstr = gnc_ctime (&secs);
    1904                 :             :         PINFO ("addr=%p set date to %" G_GUINT64_FORMAT ".%09ld %s\n",
    1905                 :             :                trans, val.tv_sec, val.tv_nsec, tstr ? tstr : "(null)");
    1906                 :             :         g_free(tstr);
    1907                 :             :     }
    1908                 :             : #endif
    1909                 :        4554 :     *dadate = val;
    1910                 :        4554 :     qof_instance_set_dirty(QOF_INSTANCE(trans));
    1911                 :        4554 :     mark_trans(trans);
    1912                 :        4554 :     xaccTransCommitEdit(trans);
    1913                 :             : 
    1914                 :             :     /* Because the date has changed, we need to make sure that each of
    1915                 :             :      * the splits is properly ordered in each of their accounts. We
    1916                 :             :      * could do that here, simply by reinserting each split into its
    1917                 :             :      * account. However, in some ways this is bad behaviour, and it
    1918                 :             :      * seems much better/nicer to defer that until the commit phase,
    1919                 :             :      * i.e. until the user has called the xaccTransCommitEdit()
    1920                 :             :      * routine. So, for now, we are done. */
    1921                 :        4554 : }
    1922                 :             : 
    1923                 :             : static inline void
    1924                 :        3336 : set_gains_date_dirty (Transaction *trans)
    1925                 :             : {
    1926                 :        3391 :     FOR_EACH_SPLIT(trans, s->gains |= GAINS_STATUS_DATE_DIRTY);
    1927                 :        3336 : }
    1928                 :             : 
    1929                 :             : void
    1930                 :        1159 : xaccTransSetDatePostedSecs (Transaction *trans, time64 secs)
    1931                 :             : {
    1932                 :        1159 :     if (!trans) return;
    1933                 :        1159 :     xaccTransSetDateInternal(trans, &trans->date_posted, secs);
    1934                 :        1159 :     set_gains_date_dirty(trans);
    1935                 :             : }
    1936                 :             : 
    1937                 :             : void
    1938                 :         101 : xaccTransSetDatePostedSecsNormalized (Transaction *trans, time64 time)
    1939                 :             : {
    1940                 :             :     GDate date;
    1941                 :         101 :     gnc_gdate_set_time64(&date, time);
    1942                 :         101 :     xaccTransSetDatePostedGDate(trans, date);
    1943                 :         101 : }
    1944                 :             : 
    1945                 :             : void
    1946                 :        2177 : xaccTransSetDatePostedGDate (Transaction *trans, GDate date)
    1947                 :             : {
    1948                 :        2177 :     if (!trans) return;
    1949                 :             : 
    1950                 :             :     /* We additionally save this date into a kvp frame to ensure in
    1951                 :             :      * the future a date which was set as *date* (without time) can
    1952                 :             :      * clearly be distinguished from the time64. */
    1953                 :        4354 :     qof_instance_set_path_kvp<GDate> (QOF_INSTANCE(trans), date, {TRANS_DATE_POSTED});
    1954                 :        2177 :     qof_instance_set_dirty (QOF_INSTANCE(trans));
    1955                 :             :     /* mark dirty and commit handled by SetDateInternal */
    1956                 :        2177 :     xaccTransSetDateInternal(trans, &trans->date_posted,
    1957                 :             :                              gdate_to_time64(date));
    1958                 :        2177 :     set_gains_date_dirty (trans);
    1959                 :             : }
    1960                 :             : 
    1961                 :             : void
    1962                 :        1218 : xaccTransSetDateEnteredSecs (Transaction *trans, time64 secs)
    1963                 :             : {
    1964                 :        1218 :     if (!trans) return;
    1965                 :        1218 :     xaccTransSetDateInternal(trans, &trans->date_entered, secs);
    1966                 :             : }
    1967                 :             : 
    1968                 :             : void
    1969                 :        1978 : xaccTransSetDate (Transaction *trans, int day, int mon, int year)
    1970                 :             : {
    1971                 :             :     GDate *date;
    1972                 :        1978 :     if (!trans) return;
    1973                 :        1978 :     date = g_date_new_dmy(day, static_cast<GDateMonth>(mon), year);
    1974                 :        1978 :     if (!g_date_valid(date))
    1975                 :             :     {
    1976                 :           0 :         PWARN("Attempted to set invalid date %d-%d-%d; set today's date instead.",
    1977                 :             :               year, mon, day);
    1978                 :           0 :         g_free(date);
    1979                 :           0 :         date = gnc_g_date_new_today();
    1980                 :             :     }
    1981                 :        1978 :     xaccTransSetDatePostedGDate(trans, *date);
    1982                 :        1978 :     g_free(date);
    1983                 :             : }
    1984                 :             : 
    1985                 :             : void
    1986                 :          67 : xaccTransSetDateDue (Transaction * trans, time64 time)
    1987                 :             : {
    1988                 :          67 :     if (!trans) return;
    1989                 :          67 :     xaccTransBeginEdit(trans);
    1990                 :         134 :     qof_instance_set_path_kvp<Time64> (QOF_INSTANCE (trans), Time64{time}, {TRANS_DATE_DUE_KVP});
    1991                 :          67 :     qof_instance_set_dirty(QOF_INSTANCE(trans));
    1992                 :          67 :     xaccTransCommitEdit(trans);
    1993                 :             : }
    1994                 :             : 
    1995                 :             : void
    1996                 :          89 : xaccTransSetTxnType (Transaction *trans, char type)
    1997                 :             : {
    1998                 :          89 :     char s[2] = {type, '\0'};
    1999                 :         178 :     set_kvp_string_path (trans, {TRANS_TXN_TYPE_KVP}, s);
    2000                 :          89 : }
    2001                 :             : 
    2002                 :          16 : void xaccTransClearReadOnly (Transaction *trans)
    2003                 :             : {
    2004                 :          32 :     set_kvp_string_path (trans, {TRANS_READ_ONLY_REASON}, nullptr);
    2005                 :          16 : }
    2006                 :             : 
    2007                 :             : void
    2008                 :         172 : xaccTransSetReadOnly (Transaction *trans, const char *reason)
    2009                 :             : {
    2010                 :         172 :     if (trans && reason)
    2011                 :         513 :         set_kvp_string_path (trans, {TRANS_READ_ONLY_REASON}, reason);
    2012                 :         172 : }
    2013                 :             : 
    2014                 :             : /********************************************************************\
    2015                 :             : \********************************************************************/
    2016                 :             : 
    2017                 :             : /* QOF does not open the trans before setting a parameter,
    2018                 :             : but the call uses check_open so we cannot use the call directly. */
    2019                 :             : static void
    2020                 :           0 : qofTransSetNum (Transaction *trans, const char *xnum)
    2021                 :             : {
    2022                 :           0 :     if (!qof_begin_edit(&trans->inst)) return;
    2023                 :           0 :     xaccTransSetNum(trans, xnum);
    2024                 :           0 :     qof_commit_edit(&trans->inst);
    2025                 :             : }
    2026                 :             : 
    2027                 :             : void
    2028                 :         845 : xaccTransSetNum (Transaction *trans, const char *xnum)
    2029                 :             : {
    2030                 :         845 :     if (!trans || !xnum) return;
    2031                 :         845 :     xaccTransBeginEdit(trans);
    2032                 :             : 
    2033                 :         845 :     CACHE_REPLACE(trans->num, xnum);
    2034                 :         845 :     qof_instance_set_dirty(QOF_INSTANCE(trans));
    2035                 :         845 :     mark_trans(trans);  /* Dirty balance of every account in trans */
    2036                 :         845 :     xaccTransCommitEdit(trans);
    2037                 :             : }
    2038                 :             : 
    2039                 :             : static void
    2040                 :           0 : qofTransSetDescription (Transaction *trans, const char *desc)
    2041                 :             : {
    2042                 :           0 :     if (!qof_begin_edit(&trans->inst)) return;
    2043                 :           0 :     xaccTransSetDescription(trans, desc);
    2044                 :           0 :     qof_commit_edit(&trans->inst);
    2045                 :             : }
    2046                 :             : 
    2047                 :             : void
    2048                 :        3329 : xaccTransSetDescription (Transaction *trans, const char *desc)
    2049                 :             : {
    2050                 :        3329 :     if (!trans || !desc) return;
    2051                 :        3325 :     xaccTransBeginEdit(trans);
    2052                 :             : 
    2053                 :        3325 :     CACHE_REPLACE(trans->description, desc);
    2054                 :        3325 :     qof_instance_set_dirty(QOF_INSTANCE(trans));
    2055                 :        3325 :     xaccTransCommitEdit(trans);
    2056                 :             : }
    2057                 :             : 
    2058                 :             : void
    2059                 :          11 : xaccTransSetDocLink (Transaction *trans, const char *doclink)
    2060                 :             : {
    2061                 :          11 :     if (!trans || !doclink) return;
    2062                 :          24 :     set_kvp_string_path (trans, {doclink_uri_str}, doclink);
    2063                 :          24 : }
    2064                 :             : 
    2065                 :             : static void
    2066                 :           0 : qofTransSetNotes (Transaction *trans, const char *notes)
    2067                 :             : {
    2068                 :           0 :     if (!qof_begin_edit(&trans->inst)) return;
    2069                 :           0 :     xaccTransSetNotes(trans, notes);
    2070                 :           0 :     qof_commit_edit(&trans->inst);
    2071                 :             : }
    2072                 :             : 
    2073                 :             : void
    2074                 :          91 : xaccTransSetNotes (Transaction *trans, const char *notes)
    2075                 :             : {
    2076                 :          91 :     if (!trans || !notes) return;
    2077                 :         258 :     set_kvp_string_path (trans, {trans_notes_str}, notes);
    2078                 :         258 : }
    2079                 :             : 
    2080                 :             : void
    2081                 :          27 : xaccTransSetIsClosingTxn (Transaction *trans, gboolean is_closing)
    2082                 :             : {
    2083                 :          27 :     xaccTransBeginEdit(trans);
    2084                 :          27 :     auto val = is_closing ? std::make_optional<int64_t>(1) : std::nullopt;
    2085                 :          81 :     qof_instance_set_path_kvp<int64_t> (QOF_INSTANCE(trans), val, {trans_is_closing_str});
    2086                 :          27 :     xaccTransCommitEdit(trans);
    2087                 :          81 : }
    2088                 :             : 
    2089                 :             : 
    2090                 :             : /********************************************************************\
    2091                 :             : \********************************************************************/
    2092                 :             : void
    2093                 :        2376 : xaccTransClearSplits(Transaction* trans)
    2094                 :             : {
    2095                 :        2376 :     xaccTransBeginEdit(trans);
    2096                 :             :     /* We only own the splits that still think they belong to us.   This is done
    2097                 :             :        in 2 steps.  In the first, the splits are marked as being destroyed, but they
    2098                 :             :        are not destroyed yet.  In the second, the destruction is committed which will
    2099                 :             :        do the actual destruction.  If both steps are done for a split before they are
    2100                 :             :        done for the next split, then a split will still be on the split list after it
    2101                 :             :        has been freed.  This can cause other parts of the code (e.g. in xaccSplitDestroy())
    2102                 :             :        to reference the split after it has been freed. */
    2103                 :        7235 :     for (auto node = trans->splits; node; node = node->next)
    2104                 :             :     {
    2105                 :        4859 :         auto s = GNC_SPLIT(node->data);
    2106                 :        4859 :         if (s && s->parent == trans)
    2107                 :             :         {
    2108                 :        4858 :             xaccSplitDestroy(s);
    2109                 :             :         }
    2110                 :             :     }
    2111                 :        7235 :     for (auto node = trans->splits; node; node = node->next)
    2112                 :             :     {
    2113                 :        4859 :         auto s = GNC_SPLIT(node->data);
    2114                 :        4859 :         if (s && s->parent == trans)
    2115                 :             :         {
    2116                 :        4858 :             xaccSplitCommitEdit(s);
    2117                 :             :         }
    2118                 :             :     }
    2119                 :        2376 :     g_list_free (trans->splits);
    2120                 :        2376 :     trans->splits = nullptr;
    2121                 :             : 
    2122                 :        2376 :     xaccTransCommitEdit(trans);
    2123                 :        2376 : }
    2124                 :             : 
    2125                 :             : Split *
    2126                 :         382 : xaccTransGetSplit (const Transaction *trans, int i)
    2127                 :             : {
    2128                 :         382 :     int j = 0;
    2129                 :         382 :     if (!trans || i < 0) return nullptr;
    2130                 :             : 
    2131                 :         547 :     FOR_EACH_SPLIT(trans, { if (i == j) return s; j++; });
    2132                 :          50 :     return nullptr;
    2133                 :             : }
    2134                 :             : 
    2135                 :             : int
    2136                 :        4887 : xaccTransGetSplitIndex(const Transaction *trans, const Split *split)
    2137                 :             : {
    2138                 :        4887 :     int j = 0;
    2139                 :        4887 :     g_return_val_if_fail(trans && split, -1);
    2140                 :             : 
    2141                 :        7569 :     FOR_EACH_SPLIT(trans, { if (s == split) return j; j++; });
    2142                 :           1 :     return -1;
    2143                 :             : }
    2144                 :             : 
    2145                 :             : SplitList *
    2146                 :        6487 : xaccTransGetSplitList (const Transaction *trans)
    2147                 :             : {
    2148                 :        6487 :     return trans ? trans->splits : nullptr;
    2149                 :             : }
    2150                 :             : 
    2151                 :             : SplitList *
    2152                 :           0 : xaccTransGetPaymentAcctSplitList (const Transaction *trans)
    2153                 :             : {
    2154                 :           0 :     GList *pay_splits = nullptr;
    2155                 :           0 :     FOR_EACH_SPLIT (trans,
    2156                 :             :                     const Account *account = xaccSplitGetAccount(s);
    2157                 :             :                     if (account && gncBusinessIsPaymentAcctType(xaccAccountGetType(account)))
    2158                 :             :                         pay_splits = g_list_prepend (pay_splits, s);
    2159                 :             :     );
    2160                 :             : 
    2161                 :           0 :     pay_splits = g_list_reverse (pay_splits);
    2162                 :           0 :     return pay_splits;
    2163                 :             : }
    2164                 :             : 
    2165                 :             : SplitList *
    2166                 :           9 : xaccTransGetAPARAcctSplitList (const Transaction *trans, gboolean strict)
    2167                 :             : {
    2168                 :           9 :     GList *apar_splits = nullptr;
    2169                 :           9 :     if (!trans) return nullptr;
    2170                 :             : 
    2171                 :          28 :     FOR_EACH_SPLIT (trans,
    2172                 :             :                     const Account *account = xaccSplitGetAccount(s);
    2173                 :             :                     if (account && xaccAccountIsAPARType(xaccAccountGetType(account)))
    2174                 :             :                     {
    2175                 :             : 
    2176                 :             :                         if (!strict)
    2177                 :             :                             apar_splits = g_list_prepend (apar_splits, s);
    2178                 :             :                         else
    2179                 :             :                         {
    2180                 :             :                             GncOwner owner;
    2181                 :             :                             GNCLot *lot = xaccSplitGetLot(s);
    2182                 :             :                             if (lot &&
    2183                 :             :                                 (gncInvoiceGetInvoiceFromLot (lot) ||
    2184                 :             :                                 gncOwnerGetOwnerFromLot (lot, &owner)))
    2185                 :             :                                 apar_splits = g_list_prepend (apar_splits, s);
    2186                 :             :                         }
    2187                 :             :                     }
    2188                 :             :     );
    2189                 :             : 
    2190                 :           9 :     apar_splits = g_list_reverse (apar_splits);
    2191                 :           9 :     return apar_splits;
    2192                 :             : }
    2193                 :             : 
    2194                 :           0 : Split *xaccTransGetFirstPaymentAcctSplit(const Transaction *trans)
    2195                 :             : {
    2196                 :           0 :     FOR_EACH_SPLIT (trans,
    2197                 :             :                     const Account *account = xaccSplitGetAccount(s);
    2198                 :             :                     if (account && gncBusinessIsPaymentAcctType(xaccAccountGetType(account)))
    2199                 :             :                         return s;
    2200                 :             :                    );
    2201                 :             : 
    2202                 :           0 :     return nullptr;
    2203                 :             : }
    2204                 :             : 
    2205                 :          22 : Split *xaccTransGetFirstAPARAcctSplit (const Transaction *trans, gboolean strict)
    2206                 :             : {
    2207                 :          40 :     FOR_EACH_SPLIT (trans,
    2208                 :             :                     const Account *account = xaccSplitGetAccount(s);
    2209                 :             :                     if (account && xaccAccountIsAPARType(xaccAccountGetType(account)))
    2210                 :             :                     {
    2211                 :             :                         GNCLot *lot;
    2212                 :             :                         GncOwner owner;
    2213                 :             : 
    2214                 :             :                         if (!strict)
    2215                 :             :                             return s;
    2216                 :             : 
    2217                 :             :                         lot = xaccSplitGetLot(s);
    2218                 :             :                         if (lot &&
    2219                 :             :                             (gncInvoiceGetInvoiceFromLot (lot) ||
    2220                 :             :                             gncOwnerGetOwnerFromLot (lot, &owner)))
    2221                 :             :                             return s;
    2222                 :             :                     }
    2223                 :             :                    );
    2224                 :             : 
    2225                 :           0 :     return nullptr;
    2226                 :             : }
    2227                 :             : 
    2228                 :             : int
    2229                 :         237 : xaccTransCountSplits (const Transaction *trans)
    2230                 :             : {
    2231                 :         237 :     gint i = 0;
    2232                 :         237 :     g_return_val_if_fail (trans != nullptr, 0);
    2233                 :         719 :     FOR_EACH_SPLIT(trans, i++);
    2234                 :         237 :     return i;
    2235                 :             : }
    2236                 :             : 
    2237                 :             : const char *
    2238                 :        2155 : xaccTransGetNum (const Transaction *trans)
    2239                 :             : {
    2240                 :        2155 :     return trans ? trans->num : nullptr;
    2241                 :             : }
    2242                 :             : 
    2243                 :             : const char *
    2244                 :        5052 : xaccTransGetDescription (const Transaction *trans)
    2245                 :             : {
    2246                 :        5052 :     return trans ? trans->description : nullptr;
    2247                 :             : }
    2248                 :             : 
    2249                 :             : const char *
    2250                 :          25 : xaccTransGetDocLink (const Transaction *trans)
    2251                 :             : {
    2252                 :         100 :     return get_kvp_string_path (trans, {doclink_uri_str});
    2253                 :          75 : }
    2254                 :             : 
    2255                 :             : const char *
    2256                 :        5702 : xaccTransGetNotes (const Transaction *trans)
    2257                 :             : {
    2258                 :       22808 :     return get_kvp_string_path (trans, {trans_notes_str});
    2259                 :       17106 : }
    2260                 :             : 
    2261                 :             : gboolean
    2262                 :      223236 : xaccTransGetIsClosingTxn (const Transaction *trans)
    2263                 :             : {
    2264                 :      669708 :     auto rv{qof_instance_get_path_kvp<int64_t> (QOF_INSTANCE(trans), {trans_is_closing_str})};
    2265                 :      446472 :     return rv ? *rv != 0 : FALSE;
    2266                 :      669708 : }
    2267                 :             : 
    2268                 :             : /********************************************************************\
    2269                 :             : \********************************************************************/
    2270                 :             : 
    2271                 :             : time64
    2272                 :      525245 : xaccTransGetDate (const Transaction *trans)
    2273                 :             : {
    2274                 :      525245 :     return trans ? trans->date_posted : 0;
    2275                 :             : }
    2276                 :             : 
    2277                 :             : /*################## Added for Reg2 #################*/
    2278                 :             : time64
    2279                 :           4 : xaccTransGetDateEntered (const Transaction *trans)
    2280                 :             : {
    2281                 :           4 :     return trans ? trans->date_entered : 0;
    2282                 :             : }
    2283                 :             : /*################## Added for Reg2 #################*/
    2284                 :             : 
    2285                 :             : time64
    2286                 :      169549 : xaccTransRetDatePosted (const Transaction *trans)
    2287                 :             : {
    2288                 :      169549 :     return trans ? trans->date_posted : 0;
    2289                 :             : }
    2290                 :             : 
    2291                 :             : GDate
    2292                 :           2 : xaccTransGetDatePostedGDate (const Transaction *trans)
    2293                 :             : {
    2294                 :             :     GDate result;
    2295                 :           2 :     g_date_clear (&result, 1);
    2296                 :           2 :     if (trans)
    2297                 :             :     {
    2298                 :             :         /* Can we look up this value in the kvp slot? If yes, use it
    2299                 :             :          * from there because it doesn't suffer from time zone
    2300                 :             :          * shifts. */
    2301                 :           6 :         if (auto res = qof_instance_get_path_kvp<GDate> (QOF_INSTANCE(trans), {TRANS_DATE_POSTED}))
    2302                 :           1 :              result = *res;
    2303                 :           2 :         if (! g_date_valid (&result) || gdate_to_time64 (result) == INT64_MAX)
    2304                 :             :         {
    2305                 :             :              /* Well, this txn doesn't have a valid GDate saved in a slot.
    2306                 :             :               * time64_to_gdate() uses local time and we want UTC so we have
    2307                 :             :               * to write it out.
    2308                 :             :               */
    2309                 :           1 :              time64 time = xaccTransGetDate(trans);
    2310                 :           1 :              struct tm *stm = gnc_gmtime(&time);
    2311                 :           1 :              if (stm)
    2312                 :             :              {
    2313                 :           1 :                  g_date_set_dmy(&result, stm->tm_mday,
    2314                 :           1 :                                 (GDateMonth)(stm->tm_mon + 1),
    2315                 :           1 :                                 stm->tm_year + 1900);
    2316                 :           1 :                  free(stm);
    2317                 :             :              }
    2318                 :             :         }
    2319                 :             :     }
    2320                 :           2 :     return result;
    2321                 :             : }
    2322                 :             : 
    2323                 :             : time64
    2324                 :         378 : xaccTransRetDateEntered (const Transaction *trans)
    2325                 :             : {
    2326                 :         378 :     return trans ? trans->date_entered : 0;
    2327                 :             : }
    2328                 :             : 
    2329                 :             : time64
    2330                 :          85 : xaccTransRetDateDue(const Transaction *trans)
    2331                 :             : {
    2332                 :          85 :     if (!trans) return 0;
    2333                 :         170 :     auto res = qof_instance_get_path_kvp<Time64> (QOF_INSTANCE (trans), {TRANS_DATE_DUE_KVP});
    2334                 :          85 :     return res ? res->t : xaccTransRetDatePosted (trans);
    2335                 :             : }
    2336                 :             : 
    2337                 :             : char
    2338                 :         360 : xaccTransGetTxnType (Transaction *trans)
    2339                 :             : {
    2340                 :         360 :     gboolean has_nonAPAR_split = FALSE;
    2341                 :             : 
    2342                 :         360 :     if (!trans) return TXN_TYPE_NONE;
    2343                 :             : 
    2344                 :         360 :     if (trans->txn_type != TXN_TYPE_UNCACHED)
    2345                 :         278 :         return trans->txn_type;
    2346                 :             : 
    2347                 :          82 :     trans->txn_type = TXN_TYPE_NONE;
    2348                 :         266 :     for (GList *n = xaccTransGetSplitList (trans); n; n = g_list_next (n))
    2349                 :             :     {
    2350                 :         184 :         Account *acc = xaccSplitGetAccount (GNC_SPLIT(n->data));
    2351                 :             : 
    2352                 :         184 :         if (!acc)
    2353                 :           0 :             continue;
    2354                 :             : 
    2355                 :         184 :         if (!xaccAccountIsAPARType (xaccAccountGetType (acc)))
    2356                 :         128 :             has_nonAPAR_split = TRUE;
    2357                 :          56 :         else if (trans->txn_type == TXN_TYPE_NONE)
    2358                 :             :         {
    2359                 :          53 :             GNCLot *lot = xaccSplitGetLot (GNC_SPLIT(n->data));
    2360                 :          53 :             GncInvoice *invoice = gncInvoiceGetInvoiceFromLot (lot);
    2361                 :             :             GncOwner owner;
    2362                 :             : 
    2363                 :          53 :             if (invoice && trans == gncInvoiceGetPostedTxn (invoice))
    2364                 :          22 :                 trans->txn_type = TXN_TYPE_INVOICE;
    2365                 :          31 :             else if (invoice || gncOwnerGetOwnerFromLot (lot, &owner))
    2366                 :          28 :                 trans->txn_type = TXN_TYPE_PAYMENT;
    2367                 :             :         }
    2368                 :             :     }
    2369                 :             : 
    2370                 :          82 :     if (!has_nonAPAR_split && (trans->txn_type == TXN_TYPE_PAYMENT))
    2371                 :           2 :         trans->txn_type = TXN_TYPE_LINK;
    2372                 :             : 
    2373                 :          82 :     return trans->txn_type;
    2374                 :             : }
    2375                 :             : 
    2376                 :             : const char *
    2377                 :        2507 : xaccTransGetReadOnly (Transaction *trans)
    2378                 :             : {
    2379                 :        7521 :     return get_kvp_string_path (trans, {TRANS_READ_ONLY_REASON});
    2380                 :             : }
    2381                 :             : 
    2382                 :             : static gboolean
    2383                 :           0 : xaccTransIsSXTemplate (const Transaction * trans)
    2384                 :             : {
    2385                 :           0 :     Split *split0 = xaccTransGetSplit (trans, 0);
    2386                 :           0 :     if (split0 != nullptr)
    2387                 :             :     {
    2388                 :           0 :         char* formula = nullptr;
    2389                 :           0 :         g_object_get (split0, "sx-debit-formula", &formula, nullptr);
    2390                 :           0 :         if (formula != nullptr)
    2391                 :             :         {
    2392                 :           0 :             g_free (formula);
    2393                 :           0 :             return TRUE;
    2394                 :             :         }
    2395                 :           0 :         g_object_get (split0, "sx-credit-formula", &formula, nullptr);
    2396                 :           0 :         if (formula != nullptr)
    2397                 :             :         {
    2398                 :           0 :             g_free (formula);
    2399                 :           0 :             return TRUE;
    2400                 :             :         }
    2401                 :             :     }
    2402                 :           0 :     return FALSE;
    2403                 :             : }
    2404                 :             : 
    2405                 :           0 : gboolean xaccTransIsReadonlyByPostedDate(const Transaction *trans)
    2406                 :             : {
    2407                 :             :     GDate *threshold_date;
    2408                 :             :     GDate trans_date;
    2409                 :           0 :     const QofBook *book = xaccTransGetBook (trans);
    2410                 :             :     gboolean result;
    2411                 :           0 :     g_assert(trans);
    2412                 :             : 
    2413                 :           0 :     if (!qof_book_uses_autoreadonly(book))
    2414                 :             :     {
    2415                 :           0 :         return FALSE;
    2416                 :             :     }
    2417                 :             : 
    2418                 :           0 :     if (xaccTransIsSXTemplate (trans))
    2419                 :           0 :         return FALSE;
    2420                 :             : 
    2421                 :           0 :     threshold_date = qof_book_get_autoreadonly_gdate(book);
    2422                 :           0 :     g_assert(threshold_date); // ok because we checked uses_autoreadonly before
    2423                 :           0 :     trans_date = xaccTransGetDatePostedGDate(trans);
    2424                 :             : 
    2425                 :             : //    g_warning("there is auto-read-only with days=%d, trans_date_day=%d, threshold_date_day=%d",
    2426                 :             : //              qof_book_get_num_days_autofreeze(book),
    2427                 :             : //              g_date_get_day(&trans_date),
    2428                 :             : //              g_date_get_day(threshold_date));
    2429                 :             : 
    2430                 :           0 :     if (g_date_compare(&trans_date, threshold_date) < 0)
    2431                 :             :     {
    2432                 :             :         //g_warning("we are auto-read-only");
    2433                 :           0 :         result = TRUE;
    2434                 :             :     }
    2435                 :             :     else
    2436                 :             :     {
    2437                 :           0 :         result = FALSE;
    2438                 :             :     }
    2439                 :           0 :     g_date_free(threshold_date);
    2440                 :           0 :     return result;
    2441                 :             : }
    2442                 :             : 
    2443                 :             : gboolean
    2444                 :           0 : xaccTransHasReconciledSplitsByAccount (const Transaction *trans,
    2445                 :             :                                        const Account *account)
    2446                 :             : {
    2447                 :             :     GList *node;
    2448                 :             : 
    2449                 :           0 :     for (node = xaccTransGetSplitList (trans); node; node = node->next)
    2450                 :             :     {
    2451                 :           0 :         Split *split = GNC_SPLIT(node->data);
    2452                 :             : 
    2453                 :           0 :         if (!xaccTransStillHasSplit(trans, split))
    2454                 :           0 :             continue;
    2455                 :           0 :         if (account && (xaccSplitGetAccount(split) != account))
    2456                 :           0 :             continue;
    2457                 :             : 
    2458                 :           0 :         switch (xaccSplitGetReconcile (split))
    2459                 :             :         {
    2460                 :           0 :         case YREC:
    2461                 :             :         case FREC:
    2462                 :           0 :             return TRUE;
    2463                 :             : 
    2464                 :           0 :         default:
    2465                 :           0 :             break;
    2466                 :             :         }
    2467                 :             :     }
    2468                 :             : 
    2469                 :           0 :     return FALSE;
    2470                 :             : }
    2471                 :             : 
    2472                 :             : gboolean
    2473                 :           0 : xaccTransHasReconciledSplits (const Transaction *trans)
    2474                 :             : {
    2475                 :           0 :     return xaccTransHasReconciledSplitsByAccount (trans, nullptr);
    2476                 :             : }
    2477                 :             : 
    2478                 :             : 
    2479                 :             : gboolean
    2480                 :           0 : xaccTransHasSplitsInStateByAccount (const Transaction *trans,
    2481                 :             :                                     const char state,
    2482                 :             :                                     const Account *account)
    2483                 :             : {
    2484                 :             :     GList *node;
    2485                 :             : 
    2486                 :           0 :     for (node = xaccTransGetSplitList (trans); node; node = node->next)
    2487                 :             :     {
    2488                 :           0 :         Split *split = GNC_SPLIT(node->data);
    2489                 :             : 
    2490                 :           0 :         if (!xaccTransStillHasSplit(trans, split))
    2491                 :           0 :             continue;
    2492                 :           0 :         if (account && (xaccSplitGetAccount(split) != account))
    2493                 :           0 :             continue;
    2494                 :             : 
    2495                 :           0 :         if (split->reconciled == state)
    2496                 :           0 :             return TRUE;
    2497                 :             :     }
    2498                 :             : 
    2499                 :           0 :     return FALSE;
    2500                 :             : }
    2501                 :             : 
    2502                 :             : gboolean
    2503                 :           0 : xaccTransHasSplitsInState (const Transaction *trans, const char state)
    2504                 :             : {
    2505                 :           0 :     return xaccTransHasSplitsInStateByAccount (trans, state, nullptr);
    2506                 :             : }
    2507                 :             : 
    2508                 :             : 
    2509                 :             : /********************************************************************\
    2510                 :             : \********************************************************************/
    2511                 :             : 
    2512                 :             : 
    2513                 :             : /* ====================================================================== */
    2514                 :             : 
    2515                 :             : static int
    2516                 :           8 : counter_thunk(Transaction *t, void *data)
    2517                 :             : {
    2518                 :           8 :     (*((guint*)data))++;
    2519                 :           8 :     return 0;
    2520                 :             : }
    2521                 :             : 
    2522                 :             : guint
    2523                 :           8 : gnc_book_count_transactions(QofBook *book)
    2524                 :             : {
    2525                 :           8 :     guint count = 0;
    2526                 :           8 :     xaccAccountTreeForEachTransaction(gnc_book_get_root_account(book),
    2527                 :             :                                       counter_thunk, (void*)&count);
    2528                 :           8 :     return count;
    2529                 :             : }
    2530                 :             : 
    2531                 :             : /********************************************************************\
    2532                 :             : \********************************************************************/
    2533                 :             : 
    2534                 :             : void
    2535                 :         103 : xaccTransVoid(Transaction *trans, const char *reason)
    2536                 :             : {
    2537                 :         104 :     g_return_if_fail(trans && reason);
    2538                 :             : 
    2539                 :             :     /* Prevent voiding transactions that are already marked
    2540                 :             :      * read only, for example generated by the business features.
    2541                 :             :      */
    2542                 :         103 :     if (xaccTransGetReadOnly (trans))
    2543                 :             :     {
    2544                 :           1 :         PWARN ("Refusing to void a read-only transaction!");
    2545                 :           1 :         return;
    2546                 :             :     }
    2547                 :         102 :     xaccTransBeginEdit(trans);
    2548                 :             : 
    2549                 :         102 :     char iso8601_str[ISO_DATELENGTH + 1] = "";
    2550                 :         102 :     gnc_time64_to_iso8601_buff (gnc_time(nullptr), iso8601_str);
    2551                 :             : 
    2552                 :         306 :     if (auto s = get_kvp_string_path (trans, {trans_notes_str}))
    2553                 :           3 :         set_kvp_string_path (trans, {void_former_notes_str}, s);
    2554                 :         408 :     set_kvp_string_path (trans, {trans_notes_str}, _("Voided transaction"));
    2555                 :         306 :     set_kvp_string_path (trans, {void_reason_str}, reason);
    2556                 :         306 :     set_kvp_string_path (trans, {void_time_str}, iso8601_str);
    2557                 :             : 
    2558                 :         306 :     FOR_EACH_SPLIT(trans, xaccSplitVoid(s));
    2559                 :             : 
    2560                 :             :     /* Dirtying taken care of by SetReadOnly */
    2561                 :         102 :     xaccTransSetReadOnly(trans, _("Transaction Voided"));
    2562                 :         102 :     xaccTransCommitEdit(trans);
    2563                 :        1022 : }
    2564                 :             : 
    2565                 :             : gboolean
    2566                 :        5857 : xaccTransGetVoidStatus(const Transaction *trans)
    2567                 :             : {
    2568                 :        5857 :     const char *s = xaccTransGetVoidReason (trans);
    2569                 :        5857 :     return (s && *s);
    2570                 :             : }
    2571                 :             : 
    2572                 :             : const char *
    2573                 :        5866 : xaccTransGetVoidReason(const Transaction *trans)
    2574                 :             : {
    2575                 :       23464 :     return get_kvp_string_path (trans, {void_reason_str});
    2576                 :       17598 : }
    2577                 :             : 
    2578                 :             : time64
    2579                 :           3 : xaccTransGetVoidTime(const Transaction *tr)
    2580                 :             : {
    2581                 :           9 :     auto void_str{get_kvp_string_path (tr, {void_time_str})};
    2582                 :           3 :     return void_str ? gnc_iso8601_to_time64_gmt (void_str) : 0;
    2583                 :           9 : }
    2584                 :             : 
    2585                 :             : void
    2586                 :           3 : xaccTransUnvoid (Transaction *trans)
    2587                 :             : {
    2588                 :           3 :     g_return_if_fail(trans);
    2589                 :             : 
    2590                 :           3 :     if (xaccTransGetVoidReason (trans) == nullptr)
    2591                 :           0 :         return; /* Transaction isn't voided. Bail. */
    2592                 :             : 
    2593                 :           3 :     xaccTransBeginEdit(trans);
    2594                 :             : 
    2595                 :          18 :     set_kvp_string_path (trans, {trans_notes_str}, get_kvp_string_path (trans, {void_former_notes_str}));
    2596                 :           9 :     set_kvp_string_path (trans, {void_former_notes_str}, nullptr);
    2597                 :           9 :     set_kvp_string_path (trans, {void_reason_str}, nullptr);
    2598                 :           9 :     set_kvp_string_path (trans, {void_time_str}, nullptr);
    2599                 :             : 
    2600                 :           9 :     FOR_EACH_SPLIT(trans, xaccSplitUnvoid(s));
    2601                 :             : 
    2602                 :             :     /* Dirtying taken care of by ClearReadOnly */
    2603                 :           3 :     xaccTransClearReadOnly(trans);
    2604                 :           3 :     xaccTransCommitEdit(trans);
    2605                 :          36 : }
    2606                 :             : 
    2607                 :             : Transaction *
    2608                 :           2 : xaccTransReverse (Transaction *orig)
    2609                 :             : {
    2610                 :             :     Transaction *trans;
    2611                 :           2 :     g_return_val_if_fail(orig, nullptr);
    2612                 :             : 
    2613                 :             :     /* First edit, dirty, and commit orig to ensure that any trading
    2614                 :             :      * splits are correctly balanced.
    2615                 :             :      */
    2616                 :           2 :     xaccTransBeginEdit (orig);
    2617                 :           2 :     qof_instance_set_dirty (QOF_INSTANCE (orig));
    2618                 :           2 :     xaccTransCommitEdit (orig);
    2619                 :             : 
    2620                 :           2 :     trans = xaccTransClone(orig);
    2621                 :           2 :     g_return_val_if_fail (trans, nullptr);
    2622                 :           2 :     xaccTransBeginEdit(trans);
    2623                 :             : 
    2624                 :             :     /* Reverse the values on each split. Clear per-split info. */
    2625                 :           6 :     FOR_EACH_SPLIT(trans,
    2626                 :             :     {
    2627                 :             :         xaccSplitSetAmount(s, gnc_numeric_neg(xaccSplitGetAmount(s)));
    2628                 :             :         xaccSplitSetValue(s, gnc_numeric_neg(xaccSplitGetValue(s)));
    2629                 :             :         xaccSplitSetReconcile(s, NREC);
    2630                 :             :     });
    2631                 :             : 
    2632                 :             :     /* Now update the original with a pointer to the new one */
    2633                 :           4 :     qof_instance_set_path_kvp<GncGUID*> (QOF_INSTANCE (orig), guid_copy(xaccTransGetGUID(trans)),
    2634                 :             :                                          {TRANS_REVERSED_BY});
    2635                 :             : 
    2636                 :             :     /* Make sure the reverse transaction is not read-only */
    2637                 :           2 :     xaccTransClearReadOnly(trans);
    2638                 :             : 
    2639                 :           2 :     qof_instance_set_dirty(QOF_INSTANCE(trans));
    2640                 :           2 :     xaccTransCommitEdit(trans);
    2641                 :           2 :     return trans;
    2642                 :             : }
    2643                 :             : 
    2644                 :             : Transaction *
    2645                 :           1 : xaccTransGetReversedBy(const Transaction *trans)
    2646                 :             : {
    2647                 :           1 :     g_return_val_if_fail(trans, nullptr);
    2648                 :           2 :     auto g = qof_instance_get_path_kvp<GncGUID*> (QOF_INSTANCE(trans), {TRANS_REVERSED_BY});
    2649                 :           1 :     return g ? xaccTransLookup (*g, qof_instance_get_book (trans)) : nullptr;
    2650                 :             : }
    2651                 :             : 
    2652                 :             : /* ============================================================== */
    2653                 :             : /** The xaccTransScrubGainsDate() routine is used to keep the posted date
    2654                 :             :  *    of gains splits in sync with the posted date of the transaction
    2655                 :             :  *    that caused the gains.
    2656                 :             :  *
    2657                 :             :  *    The posted date is kept in sync using a lazy-evaluation scheme.
    2658                 :             :  *    If xaccTransactionSetDatePosted() is called, the date change is
    2659                 :             :  *    accepted, and the split is marked date-dirty.  If the posted date
    2660                 :             :  *    is queried for (using GetDatePosted()), then the transaction is
    2661                 :             :  *    evaluated. If it's a gains-transaction, then its date is copied
    2662                 :             :  *    from the source transaction that created the gains.
    2663                 :             :  */
    2664                 :             : 
    2665                 :             : static void
    2666                 :           3 : xaccTransScrubGainsDate (Transaction *trans)
    2667                 :             : {
    2668                 :             :     SplitList *node;
    2669                 :           3 :     SplitList *splits_copy = g_list_copy(trans->splits);
    2670                 :           9 :     for (node = splits_copy; node; node = node->next)
    2671                 :             :     {
    2672                 :           6 :         Split *s = GNC_SPLIT(node->data);
    2673                 :             : 
    2674                 :           6 :         if (!xaccTransStillHasSplit(trans, s)) continue;
    2675                 :           6 :         xaccSplitDetermineGainStatus(s);
    2676                 :             : 
    2677                 :           6 :         if ((GAINS_STATUS_GAINS & s->gains) &&
    2678                 :           3 :             s->gains_split &&
    2679                 :           3 :             ((s->gains_split->gains & GAINS_STATUS_DATE_DIRTY) ||
    2680                 :           2 :              (s->gains & GAINS_STATUS_DATE_DIRTY)))
    2681                 :             :         {
    2682                 :           2 :             Transaction *source_trans = s->gains_split->parent;
    2683                 :           2 :             s->gains &= ~GAINS_STATUS_DATE_DIRTY;
    2684                 :           2 :             s->gains_split->gains &= ~GAINS_STATUS_DATE_DIRTY;
    2685                 :           2 :             xaccTransSetDatePostedSecs(trans, source_trans->date_posted);
    2686                 :           6 :             FOR_EACH_SPLIT(trans, s->gains &= ~GAINS_STATUS_DATE_DIRTY);
    2687                 :             :         }
    2688                 :             :     }
    2689                 :           3 :     g_list_free(splits_copy);
    2690                 :           3 : }
    2691                 :             : 
    2692                 :             : /* ============================================================== */
    2693                 :             : 
    2694                 :             : void
    2695                 :           0 : xaccTransScrubGains (Transaction *trans, Account *gain_acc)
    2696                 :             : {
    2697                 :             :     SplitList *node;
    2698                 :             : 
    2699                 :           0 :     ENTER("(trans=%p)", trans);
    2700                 :             :     /* Lock down posted date, its to be synced to the posted date
    2701                 :             :      * for the source of the cap gains. */
    2702                 :           0 :     xaccTransScrubGainsDate(trans);
    2703                 :             : 
    2704                 :             :     /* Fix up the split amount */
    2705                 :           0 : restart:
    2706                 :           0 :     for (node = trans->splits; node; node = node->next)
    2707                 :             :     {
    2708                 :           0 :         Split *s = GNC_SPLIT(node->data);
    2709                 :             : 
    2710                 :           0 :         if (!xaccTransStillHasSplit(trans, s)) continue;
    2711                 :             : 
    2712                 :           0 :         xaccSplitDetermineGainStatus(s);
    2713                 :           0 :         if (s->gains & GAINS_STATUS_ADIRTY)
    2714                 :             :         {
    2715                 :           0 :             gboolean altered = FALSE;
    2716                 :           0 :             s->gains &= ~GAINS_STATUS_ADIRTY;
    2717                 :           0 :             if (s->lot)
    2718                 :           0 :                 altered = xaccScrubLot(s->lot);
    2719                 :             :             else
    2720                 :           0 :                 altered = xaccSplitAssign(s);
    2721                 :           0 :             if (altered) goto restart;
    2722                 :             :         }
    2723                 :             :     }
    2724                 :             : 
    2725                 :             :     /* Fix up gains split value */
    2726                 :           0 :     FOR_EACH_SPLIT(trans,
    2727                 :             :                    if ((s->gains & GAINS_STATUS_VDIRTY) ||
    2728                 :             :                        (s->gains_split &&
    2729                 :             :                         (s->gains_split->gains & GAINS_STATUS_VDIRTY)))
    2730                 :             :                        xaccSplitComputeCapGains(s, gain_acc);
    2731                 :             :         );
    2732                 :             : 
    2733                 :           0 :     LEAVE("(trans=%p)", trans);
    2734                 :           0 : }
    2735                 :             : 
    2736                 :             : Split *
    2737                 :          50 : xaccTransFindSplitByAccount(const Transaction *trans, const Account *acc)
    2738                 :             : {
    2739                 :          50 :     if (!trans || !acc) return nullptr;
    2740                 :         134 :     FOR_EACH_SPLIT(trans, if (xaccSplitGetAccount(s) == acc) return s);
    2741                 :          31 :     return nullptr;
    2742                 :             : }
    2743                 :             : 
    2744                 :             : static void
    2745                 :           0 : record_price (Split *split,
    2746                 :             :               PriceSource source)
    2747                 :             : {
    2748                 :             :     Transaction *trans;
    2749                 :             :     Account *account;
    2750                 :             :     QofBook* book;
    2751                 :             :     GNCPriceDB* pricedb;
    2752                 :             :     gnc_commodity* comm;
    2753                 :             :     gnc_commodity* curr;
    2754                 :             :     GNCPrice* price;
    2755                 :             :     gnc_numeric price_value, value, amount;
    2756                 :             :     int scu;
    2757                 :             :     time64 time;
    2758                 :             :     gboolean swap;
    2759                 :             : 
    2760                 :           0 :     account = xaccSplitGetAccount (split);
    2761                 :           0 :     if (!xaccAccountIsPriced (account))
    2762                 :             :     {
    2763                 :           0 :        return;
    2764                 :             :     }
    2765                 :           0 :     amount = xaccSplitGetAmount (split);
    2766                 :           0 :     if (gnc_numeric_zero_p (amount))
    2767                 :             :     {
    2768                 :           0 :        return;
    2769                 :             :     }
    2770                 :           0 :     trans = xaccSplitGetParent (split);
    2771                 :           0 :     value = gnc_numeric_div (xaccSplitGetValue (split), amount,
    2772                 :             :                              GNC_DENOM_AUTO,
    2773                 :             :                              GNC_HOW_DENOM_EXACT);
    2774                 :           0 :     book = qof_instance_get_book (QOF_INSTANCE (account));
    2775                 :           0 :     pricedb = gnc_pricedb_get_db (book);
    2776                 :           0 :     comm = xaccAccountGetCommodity (account);
    2777                 :           0 :     curr = xaccTransGetCurrency (trans);
    2778                 :           0 :     scu = gnc_commodity_get_fraction (curr);
    2779                 :           0 :     swap = FALSE;
    2780                 :           0 :     time = xaccTransGetDate (trans);
    2781                 :           0 :     price = gnc_pricedb_lookup_day_t64 (pricedb, comm, curr, time);
    2782                 :           0 :     if (gnc_commodity_equiv (comm, gnc_price_get_currency (price)))
    2783                 :           0 :         swap = TRUE;
    2784                 :             : 
    2785                 :           0 :     if (price)
    2786                 :             :     {
    2787                 :           0 :         PriceSource oldsource = gnc_price_get_source (price);
    2788                 :           0 :         price_value = gnc_price_get_value (price);
    2789                 :           0 :         if (gnc_numeric_equal (swap ? gnc_numeric_invert (value) : value,
    2790                 :             :                                price_value))
    2791                 :             :         {
    2792                 :           0 :             gnc_price_unref (price);
    2793                 :           0 :             return;
    2794                 :             :         }
    2795                 :           0 :         if (oldsource < source &&
    2796                 :           0 :             !(oldsource == PRICE_SOURCE_XFER_DLG_VAL &&
    2797                 :             :              source == PRICE_SOURCE_SPLIT_REG))
    2798                 :             :         {
    2799                 :             :             /* Existing price is preferred over this one. */
    2800                 :           0 :             gnc_price_unref (price);
    2801                 :           0 :             return;
    2802                 :             :         }
    2803                 :           0 :         if (swap)
    2804                 :             :         {
    2805                 :           0 :             value = gnc_numeric_invert (value);
    2806                 :           0 :             scu = gnc_commodity_get_fraction (comm);
    2807                 :             :         }
    2808                 :           0 :         value = gnc_numeric_convert (value, scu * COMMODITY_DENOM_MULT,
    2809                 :             :                                      GNC_HOW_RND_ROUND_HALF_UP);
    2810                 :           0 :         gnc_price_begin_edit (price);
    2811                 :           0 :         gnc_price_set_time64 (price, time);
    2812                 :           0 :         gnc_price_set_source (price, source);
    2813                 :           0 :         gnc_price_set_typestr (price, PRICE_TYPE_TRN);
    2814                 :           0 :         gnc_price_set_value (price, value);
    2815                 :           0 :         gnc_price_commit_edit (price);
    2816                 :           0 :         gnc_price_unref (price);
    2817                 :           0 :         return;
    2818                 :             :     }
    2819                 :             : 
    2820                 :           0 :     value = gnc_numeric_convert (value, scu * COMMODITY_DENOM_MULT,
    2821                 :             :                                  GNC_HOW_RND_ROUND_HALF_UP);
    2822                 :           0 :     price = gnc_price_create (book);
    2823                 :           0 :     gnc_price_begin_edit (price);
    2824                 :           0 :     gnc_price_set_commodity (price, comm);
    2825                 :           0 :     gnc_price_set_currency (price, curr);
    2826                 :           0 :     gnc_price_set_time64 (price, time);
    2827                 :           0 :     gnc_price_set_source (price, source);
    2828                 :           0 :     gnc_price_set_typestr (price, PRICE_TYPE_TRN);
    2829                 :           0 :     gnc_price_set_value (price, value);
    2830                 :           0 :     gnc_pricedb_add_price (pricedb, price);
    2831                 :           0 :     gnc_price_commit_edit (price);
    2832                 :             : }
    2833                 :             : 
    2834                 :             : void
    2835                 :           0 : xaccTransRecordPrice (Transaction *trans, PriceSource source)
    2836                 :             : {
    2837                 :             :    /* XXX: This should have been part of xaccSplitCommitEdit. */
    2838                 :           0 :     g_list_foreach (xaccTransGetSplitList (trans), (GFunc)record_price, (gpointer)source);
    2839                 :           0 : }
    2840                 :             : 
    2841                 :             : /********************************************************************\
    2842                 :             : \********************************************************************/
    2843                 :             : /* QofObject function implementation */
    2844                 :             : 
    2845                 :             : static void
    2846                 :        2255 : destroy_tx_on_book_close(QofInstance *ent, gpointer data)
    2847                 :             : {
    2848                 :        2255 :     Transaction* tx = GNC_TRANSACTION(ent);
    2849                 :             : 
    2850                 :        2255 :     xaccTransDestroy(tx);
    2851                 :        2255 : }
    2852                 :             : 
    2853                 :             : static int
    2854                 :       10470 : trans_reverse_order (const Transaction* a, const Transaction* b)
    2855                 :             : {
    2856                 :       10470 :     return xaccTransOrder (b, a);
    2857                 :             : }
    2858                 :             : 
    2859                 :             : /** Handles book end - frees all transactions from the book
    2860                 :             :  *
    2861                 :             :  * @param book Book being closed
    2862                 :             :  */
    2863                 :             : static void
    2864                 :         154 : gnc_transaction_book_end(QofBook* book)
    2865                 :             : {
    2866                 :             :     QofCollection *col;
    2867                 :             : 
    2868                 :         154 :     col = qof_book_get_collection(book, GNC_ID_TRANS);
    2869                 :             : 
    2870                 :             :     // destroy all transactions from latest to earliest, because
    2871                 :             :     // accounts' splits are stored chronologically and removing from
    2872                 :             :     // the end is faster than from the middle.
    2873                 :         154 :     qof_collection_foreach_sorted (col, destroy_tx_on_book_close, nullptr,
    2874                 :             :                                    (GCompareFunc)trans_reverse_order);
    2875                 :         154 : }
    2876                 :             : 
    2877                 :             : #ifdef _MSC_VER
    2878                 :             : /* MSVC compiler doesn't have C99 "designated initializers"
    2879                 :             :  * so we wrap them in a macro that is empty on MSVC. */
    2880                 :             : # define DI(x) /* */
    2881                 :             : #else
    2882                 :             : # define DI(x) x
    2883                 :             : #endif
    2884                 :             : 
    2885                 :             : /* Hook into the QofObject registry */
    2886                 :             : static QofObject trans_object_def =
    2887                 :             : {
    2888                 :             :     DI(.interface_version = ) QOF_OBJECT_VERSION,
    2889                 :             :     DI(.e_type            = ) GNC_ID_TRANS,
    2890                 :             :     DI(.type_label        = ) "Transaction",
    2891                 :             :     DI(.create            = ) (void* (*)(QofBook*))xaccMallocTransaction,
    2892                 :             :     DI(.book_begin        = ) nullptr,
    2893                 :             :     DI(.book_end          = ) gnc_transaction_book_end,
    2894                 :             :     DI(.is_dirty          = ) qof_collection_is_dirty,
    2895                 :             :     DI(.mark_clean        = ) qof_collection_mark_clean,
    2896                 :             :     DI(.foreach           = ) qof_collection_foreach,
    2897                 :             :     DI(.printable         = ) (const char * (*)(gpointer)) xaccTransGetDescription,
    2898                 :             :     DI(.version_cmp       = ) (int (*)(gpointer, gpointer)) qof_instance_version_cmp,
    2899                 :             : };
    2900                 :             : 
    2901                 :             : static gboolean
    2902                 :           0 : trans_is_balanced_p (const Transaction *trans)
    2903                 :             : {
    2904                 :           0 :     return trans ? xaccTransIsBalanced(trans) : FALSE;
    2905                 :             : }
    2906                 :             : 
    2907                 :          82 : gboolean xaccTransRegister (void)
    2908                 :             : {
    2909                 :             :     static QofParam params[] =
    2910                 :             :         {
    2911                 :             :             {
    2912                 :             :                 TRANS_NUM, QOF_TYPE_STRING,
    2913                 :             :                 (QofAccessFunc)xaccTransGetNum,
    2914                 :             :                 (QofSetterFunc)qofTransSetNum,
    2915                 :             :                 qof_string_number_compare_func
    2916                 :             :             },
    2917                 :             :             {
    2918                 :             :                 TRANS_DESCRIPTION, QOF_TYPE_STRING,
    2919                 :             :                 (QofAccessFunc)xaccTransGetDescription,
    2920                 :             :                 (QofSetterFunc)qofTransSetDescription
    2921                 :             :             },
    2922                 :             :             {
    2923                 :             :                 TRANS_DATE_ENTERED, QOF_TYPE_DATE,
    2924                 :             :                 (QofAccessFunc)xaccTransRetDateEntered,
    2925                 :             :                 (QofSetterFunc)xaccTransSetDateEnteredSecs
    2926                 :             :             },
    2927                 :             :             {
    2928                 :             :                 TRANS_DATE_POSTED, QOF_TYPE_DATE,
    2929                 :             :                 (QofAccessFunc)xaccTransRetDatePosted,
    2930                 :             :                 (QofSetterFunc)xaccTransSetDatePostedSecs
    2931                 :             :             },
    2932                 :             :             {
    2933                 :             :                 TRANS_DATE_DUE, QOF_TYPE_DATE,
    2934                 :             :                 (QofAccessFunc)xaccTransRetDateDue, nullptr
    2935                 :             :             },
    2936                 :             :             {
    2937                 :             :                 TRANS_IMBALANCE, QOF_TYPE_NUMERIC,
    2938                 :             :                 (QofAccessFunc)xaccTransGetImbalanceValue, nullptr
    2939                 :             :             },
    2940                 :             :             {
    2941                 :             :                 TRANS_NOTES, QOF_TYPE_STRING,
    2942                 :             :                 (QofAccessFunc)xaccTransGetNotes,
    2943                 :             :                 (QofSetterFunc)qofTransSetNotes
    2944                 :             :             },
    2945                 :             :             {
    2946                 :             :                 TRANS_DOCLINK, QOF_TYPE_STRING,
    2947                 :             :                 (QofAccessFunc)xaccTransGetDocLink,
    2948                 :             :                 (QofSetterFunc)xaccTransSetDocLink
    2949                 :             :             },
    2950                 :             :             {
    2951                 :             :                 TRANS_IS_CLOSING, QOF_TYPE_BOOLEAN,
    2952                 :             :                 (QofAccessFunc)xaccTransGetIsClosingTxn, nullptr
    2953                 :             :             },
    2954                 :             :             {
    2955                 :             :                 TRANS_IS_BALANCED, QOF_TYPE_BOOLEAN,
    2956                 :             :                 (QofAccessFunc)trans_is_balanced_p, nullptr
    2957                 :             :             },
    2958                 :             :             {
    2959                 :             :                 TRANS_TYPE, QOF_TYPE_CHAR,
    2960                 :             :                 (QofAccessFunc)xaccTransGetTxnType,
    2961                 :             :                 (QofSetterFunc)xaccTransSetTxnType
    2962                 :             :             },
    2963                 :             :             {
    2964                 :             :                 TRANS_VOID_STATUS, QOF_TYPE_BOOLEAN,
    2965                 :             :                 (QofAccessFunc)xaccTransGetVoidStatus, nullptr
    2966                 :             :             },
    2967                 :             :             {
    2968                 :             :                 TRANS_VOID_REASON, QOF_TYPE_STRING,
    2969                 :             :                 (QofAccessFunc)xaccTransGetVoidReason, nullptr
    2970                 :             :             },
    2971                 :             :             {
    2972                 :             :                 TRANS_VOID_TIME, QOF_TYPE_DATE,
    2973                 :             :                 (QofAccessFunc)xaccTransGetVoidTime, nullptr
    2974                 :             :             },
    2975                 :             :             {
    2976                 :             :                 TRANS_SPLITLIST, GNC_ID_SPLIT,
    2977                 :             :                 (QofAccessFunc)xaccTransGetSplitList, nullptr
    2978                 :             :             },
    2979                 :             :             {
    2980                 :             :                 QOF_PARAM_BOOK, QOF_ID_BOOK,
    2981                 :             :                 (QofAccessFunc)qof_instance_get_book, nullptr
    2982                 :             :             },
    2983                 :             :             {
    2984                 :             :                 QOF_PARAM_GUID, QOF_TYPE_GUID,
    2985                 :             :                 (QofAccessFunc)qof_entity_get_guid, nullptr
    2986                 :             :             },
    2987                 :             :             { nullptr },
    2988                 :             :         };
    2989                 :             : 
    2990                 :          82 :     qof_class_register (GNC_ID_TRANS, (QofSortFunc)xaccTransOrder, params);
    2991                 :             : 
    2992                 :          82 :     return qof_object_register (&trans_object_def);
    2993                 :             : }
    2994                 :             : 
    2995                 :             : TransTestFunctions*
    2996                 :          43 : _utest_trans_fill_functions (void)
    2997                 :             : {
    2998                 :          43 :     TransTestFunctions *func = g_new (TransTestFunctions, 1);
    2999                 :             : 
    3000                 :          43 :     func->mark_trans = mark_trans;
    3001                 :          43 :     func->gen_event_trans = gen_event_trans;
    3002                 :          43 :     func->xaccFreeTransaction = xaccFreeTransaction;
    3003                 :          43 :     func->destroy_gains = destroy_gains;
    3004                 :          43 :     func->do_destroy = do_destroy;
    3005                 :          43 :     func->was_trans_emptied = was_trans_emptied;
    3006                 :          43 :     func->trans_on_error = trans_on_error;
    3007                 :          43 :     func->trans_cleanup_commit = trans_cleanup_commit;
    3008                 :          43 :     func->xaccTransScrubGainsDate = xaccTransScrubGainsDate;
    3009                 :          43 :     func->dupe_trans = dupe_trans;
    3010                 :          43 :     return func;
    3011                 :             : }
    3012                 :             : 
    3013                 :             : /************************ END OF ************************************\
    3014                 :             : \************************* FILE *************************************/
        

Generated by: LCOV version 2.0-1