LCOV - code coverage report
Current view: top level - libgnucash/engine - Transaction.cpp (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 81.8 % 1096 897
Test Date: 2026-11-02 18:19:43 Functions: 86.3 % 124 107
Legend: Lines:     hit not hit

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

Generated by: LCOV version 2.0-1