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

Generated by: LCOV version 2.0-1