LCOV - code coverage report
Current view: top level - libgnucash/engine - cap-gains.cpp (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 78.3 % 346 271
Test Date: 2025-02-07 16:25:45 Functions: 78.6 % 14 11
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : /********************************************************************\
       2                 :             :  * cap-gains.c -- Automatically Compute Capital Gains/Losses        *
       3                 :             :  *                                                                  *
       4                 :             :  * This program is free software; you can redistribute it and/or    *
       5                 :             :  * modify it under the terms of the GNU General Public License as   *
       6                 :             :  * published by the Free Software Foundation; either version 2 of   *
       7                 :             :  * the License, or (at your option) any later version.              *
       8                 :             :  *                                                                  *
       9                 :             :  * This program is distributed in the hope that it will be useful,  *
      10                 :             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
      11                 :             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
      12                 :             :  * GNU General Public License for more details.                     *
      13                 :             :  *                                                                  *
      14                 :             :  * You should have received a copy of the GNU General Public License*
      15                 :             :  * along with this program; if not, contact:                        *
      16                 :             :  *                                                                  *
      17                 :             :  * Free Software Foundation           Voice:  +1-617-542-5942       *
      18                 :             :  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
      19                 :             :  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
      20                 :             : \********************************************************************/
      21                 :             : 
      22                 :             : /** @file cap-gains.c
      23                 :             :  *  @brief Utilities to Automatically Compute Capital Gains/Losses.
      24                 :             :  *  @author Created by Linas Vepstas August 2003
      25                 :             :  *  @author Copyright (c) 2003,2004 Linas Vepstas <linas@linas.org>
      26                 :             :  *
      27                 :             :  *  This file implements the various routines to automatically
      28                 :             :  *  compute and handle Cap Gains/Losses resulting from trading
      29                 :             :  *  activities.  Some of these routines might have broader
      30                 :             :  *  applicability, for handling depreciation & etc.
      31                 :             :  *
      32                 :             :  *  This code is under development, and is 'beta': we think we're
      33                 :             :  *  mostly done, and we've tested and "things work for us", but there
      34                 :             :  *  may still be something missing, and there might still be some
      35                 :             :  *  bugs.
      36                 :             :  *
      37                 :             :  * This code uses a 'gains dirty' flag: A 'dirty' flag on the source
      38                 :             :  * split indicates that the gains transaction needs to be recomputed.
      39                 :             :  * Another flag, the gains transaction flag, marks the split as
      40                 :             :  * being a gains split, and that the source transaction should be
      41                 :             :  * checked for dirtiness before returning the date, the amount, the
      42                 :             :  * value, etc.  Finally, these flags make amount and value read-only
      43                 :             :  * for the gains splits. (the memo is user-modifieable).
      44                 :             :  *
      45                 :             :  * If the amount in a split is changed, then the lot has to be recomputed.
      46                 :             :  * This has a potential trickle-through effect on all later lots.
      47                 :             :  * Ideally, later lots are dissolved, and recomputed.  However, some
      48                 :             :  * lots may have been user-hand-built. These should be left alone.
      49                 :             :  *
      50                 :             : ToDo:
      51                 :             :  o XXX Need to create a data-integrity scrubber, that makes sure that
      52                 :             :    the various flags, and pointers & etc. match. See sections marked
      53                 :             :    with XXX below for things that might go wrong.
      54                 :             :  */
      55                 :             : 
      56                 :             : #include <config.h>
      57                 :             : 
      58                 :             : #include <glib.h>
      59                 :             : #include <glib/gi18n.h>
      60                 :             : 
      61                 :             : #include "Account.hpp"
      62                 :             : #include "AccountP.hpp"
      63                 :             : #include "Scrub2.h"
      64                 :             : #include "Scrub3.h"
      65                 :             : #include "Transaction.h"
      66                 :             : #include "TransactionP.hpp"
      67                 :             : #include "cap-gains.h"
      68                 :             : #include "gnc-engine.h"
      69                 :             : #include "engine-helpers.h"
      70                 :             : #include "gnc-lot.h"
      71                 :             : #include "policy.h"
      72                 :             : #include "policy-p.h"
      73                 :             : 
      74                 :             : static QofLogModule log_module = GNC_MOD_LOT;
      75                 :             : 
      76                 :             : 
      77                 :             : /* ============================================================== */
      78                 :             : 
      79                 :             : gboolean
      80                 :          64 : xaccAccountHasTrades (const Account *acc)
      81                 :             : {
      82                 :             :     gnc_commodity *acc_comm;
      83                 :             : 
      84                 :          64 :     if (!acc) return FALSE;
      85                 :             : 
      86                 :          64 :     if (xaccAccountIsPriced (acc))
      87                 :          28 :         return TRUE;
      88                 :             : 
      89                 :          36 :     acc_comm = xaccAccountGetCommodity(acc);
      90                 :             : 
      91                 :          38 :     for (auto s : xaccAccountGetSplits (acc))
      92                 :             :     {
      93                 :          35 :         Transaction *t = s->parent;
      94                 :          35 :         if (s->gains == GAINS_STATUS_GAINS) continue;
      95                 :          35 :         if (acc_comm != t->common_currency) return TRUE;
      96                 :             :     }
      97                 :             : 
      98                 :           3 :     return FALSE;
      99                 :             : }
     100                 :             : 
     101                 :             : /* ============================================================== */
     102                 :             : 
     103                 :             : struct FindLot
     104                 :             : {
     105                 :             :     GNCLot *lot;
     106                 :             :     gnc_commodity *currency;
     107                 :             :     time64 time;
     108                 :             :     int (*numeric_pred)(gnc_numeric);
     109                 :             :     gboolean (*date_pred)(time64 e, time64 tr);
     110                 :             : };
     111                 :             : 
     112                 :             : static gboolean
     113                 :           9 : earliest_pred (time64 earl, time64 tran)
     114                 :             : {
     115                 :           9 :     return earl > tran;
     116                 :             : }
     117                 :             : 
     118                 :             : static gboolean
     119                 :           0 : latest_pred (time64 earl, time64 tran)
     120                 :             : {
     121                 :           0 :     return earl < tran;
     122                 :             : }
     123                 :             : 
     124                 :             : static gpointer
     125                 :         755 : finder_helper (GNCLot *lot,  gpointer user_data)
     126                 :             : {
     127                 :         755 :     auto els = static_cast<FindLot*>(user_data);
     128                 :             :     Split *s;
     129                 :             :     Transaction *trans;
     130                 :             :     gnc_numeric bal;
     131                 :             :     gboolean opening_is_positive, bal_is_positive;
     132                 :         755 :     time64 posted = 0;
     133                 :             : 
     134                 :         755 :     if (gnc_lot_is_closed (lot)) return nullptr;
     135                 :             : 
     136                 :         709 :     s = gnc_lot_get_earliest_split (lot);
     137                 :         709 :     if (s == nullptr) return nullptr;
     138                 :             : 
     139                 :             :     /* We want a lot whose balance is of the correct sign.  All splits
     140                 :             :        in a lot must be the opposite sign of the opening split.  We also
     141                 :             :        want to ignore lots that are overfull, i.e., where the balance in
     142                 :             :        the lot is of opposite sign to the opening split in the lot. */
     143                 :         709 :     if (0 == (els->numeric_pred) (s->amount)) return nullptr;
     144                 :         367 :     bal = gnc_lot_get_balance (lot);
     145                 :         367 :     opening_is_positive = gnc_numeric_positive_p (s->amount);
     146                 :         367 :     bal_is_positive = gnc_numeric_positive_p (bal);
     147                 :         367 :     if (opening_is_positive != bal_is_positive) return nullptr;
     148                 :             : 
     149                 :         367 :     trans = s->parent;
     150                 :         734 :     if (els->currency &&
     151                 :         367 :             (FALSE == gnc_commodity_equiv (els->currency,
     152                 :         367 :                                            trans->common_currency)))
     153                 :             :     {
     154                 :         358 :         return nullptr;
     155                 :             :     }
     156                 :             : 
     157                 :           9 :     posted = trans->date_posted;
     158                 :           9 :     if (els->date_pred (els->time, posted))
     159                 :             :     {
     160                 :           9 :         els->time = trans->date_posted;
     161                 :           9 :         els->lot = lot;
     162                 :             :     }
     163                 :             : 
     164                 :           9 :     return nullptr;
     165                 :             : }
     166                 :             : 
     167                 :             : static inline GNCLot *
     168                 :          60 : xaccAccountFindOpenLot (Account *acc, gnc_numeric sign,
     169                 :             :                         gnc_commodity *currency,
     170                 :             :                         gint64 guess,
     171                 :             :                         gboolean (*date_pred)(time64, time64))
     172                 :             : {
     173                 :             :     FindLot es;
     174                 :             : 
     175                 :          60 :     es.lot = nullptr;
     176                 :          60 :     es.currency = currency;
     177                 :          60 :     es.time = guess;
     178                 :          60 :     es.date_pred = date_pred;
     179                 :             : 
     180                 :          60 :     if (gnc_numeric_positive_p(sign)) es.numeric_pred = gnc_numeric_negative_p;
     181                 :          32 :     else es.numeric_pred = gnc_numeric_positive_p;
     182                 :             : 
     183                 :          60 :     xaccAccountForEachLot (acc, finder_helper, &es);
     184                 :          60 :     return es.lot;
     185                 :             : }
     186                 :             : 
     187                 :             : GNCLot *
     188                 :          60 : xaccAccountFindEarliestOpenLot (Account *acc, gnc_numeric sign,
     189                 :             :                                 gnc_commodity *currency)
     190                 :             : {
     191                 :             :     GNCLot *lot;
     192                 :          60 :     ENTER (" sign=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, sign.num,
     193                 :             :            sign.denom);
     194                 :             : 
     195                 :          60 :     lot = xaccAccountFindOpenLot (acc, sign, currency,
     196                 :             :                                   G_MAXINT64, earliest_pred);
     197                 :          60 :     LEAVE ("found lot=%p %s baln=%s", lot, gnc_lot_get_title (lot),
     198                 :             :            gnc_num_dbg_to_string(gnc_lot_get_balance(lot)));
     199                 :          60 :     return lot;
     200                 :             : }
     201                 :             : 
     202                 :             : GNCLot *
     203                 :           0 : xaccAccountFindLatestOpenLot (Account *acc, gnc_numeric sign,
     204                 :             :                               gnc_commodity *currency)
     205                 :             : {
     206                 :             :     GNCLot *lot;
     207                 :           0 :     ENTER (" sign=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT,
     208                 :             :            sign.num, sign.denom);
     209                 :             : 
     210                 :           0 :     lot = xaccAccountFindOpenLot (acc, sign, currency,
     211                 :             :                                   G_MININT64, latest_pred);
     212                 :           0 :     LEAVE ("found lot=%p %s", lot, gnc_lot_get_title (lot));
     213                 :           0 :     return lot;
     214                 :             : }
     215                 :             : 
     216                 :             : /* ============================================================== */
     217                 :             : 
     218                 :             : Split *
     219                 :          60 : xaccSplitAssignToLot (Split *split, GNCLot *lot)
     220                 :             : {
     221                 :             :     Account *acc;
     222                 :             :     gnc_numeric baln;
     223                 :             :     int cmp;
     224                 :             :     gboolean baln_is_positive, amt_is_positive;
     225                 :             : 
     226                 :          60 :     if (!lot) return split;
     227                 :          60 :     if (!split) return nullptr;
     228                 :             : 
     229                 :             :     /* If this split already belongs to a lot, we are done. */
     230                 :          60 :     if (split->lot) return nullptr;
     231                 :             : 
     232                 :             :     /* Anomalous situation; except for voided transactions,
     233                 :             :      * we don't expect to see splits with no amount ..
     234                 :             :      * unless they're gains splits, and we shouldn't see those.
     235                 :             :      */
     236                 :          60 :     if (gnc_numeric_zero_p (split->amount))
     237                 :             :     {
     238                 :           0 :         if (xaccTransGetVoidStatus(split->parent)) return nullptr;
     239                 :             : 
     240                 :           0 :         PWARN ("split with zero amount; value=%s gflag=%x gsplit=%p",
     241                 :             :                gnc_num_dbg_to_string (split->amount),
     242                 :             :                split->gains,
     243                 :             :                split->gains_split);
     244                 :           0 :         if (split->gains_split)
     245                 :             :         {
     246                 :           0 :             PWARN ("gains amt=%s value=%s",
     247                 :             :                    gnc_num_dbg_to_string (split->gains_split->amount),
     248                 :             :                    gnc_num_dbg_to_string (split->gains_split->value));
     249                 :             :         }
     250                 :           0 :         return nullptr;
     251                 :             :     }
     252                 :             : 
     253                 :             :     /* If the lot is closed, we can't add anything to it */
     254                 :          60 :     baln = gnc_lot_get_balance (lot);
     255                 :          60 :     if (gnc_lot_is_closed (lot)) return split;
     256                 :             : 
     257                 :             :     /* If the lot balance is zero, but the lot is open, then
     258                 :             :      * the lot is empty. Unconditionally add the split. */
     259                 :          60 :     if (gnc_numeric_zero_p (baln))
     260                 :             :     {
     261                 :          53 :         acc = split->acc;
     262                 :          53 :         xaccAccountBeginEdit (acc);
     263                 :          53 :         gnc_lot_add_split (lot, split);
     264                 :          53 :         PINFO ("added split to empty lot, new lot baln=%s (%s)",
     265                 :             :                gnc_num_dbg_to_string (gnc_lot_get_balance(lot)),
     266                 :             :                gnc_lot_get_title (lot));
     267                 :          53 :         xaccAccountCommitEdit (acc);
     268                 :          53 :         return nullptr;
     269                 :             :     }
     270                 :             : 
     271                 :             :     /* If the sign of the split is the same as the sign of the lot,
     272                 :             :      * add the split, but complain about it ... none of the currently
     273                 :             :      * implemented accounting policies should be giving us splits
     274                 :             :      * that make lots larger.  One a lot is open, the FIFO/LIFO
     275                 :             :      * policies should be working only to make the lot smaller.
     276                 :             :      * We can remove the warning emssage come the day we have
     277                 :             :      * fancier policies.
     278                 :             :      */
     279                 :           7 :     baln_is_positive = gnc_numeric_positive_p (baln);
     280                 :           7 :     amt_is_positive = gnc_numeric_positive_p (split->amount);
     281                 :           7 :     if ((baln_is_positive && amt_is_positive) ||
     282                 :           3 :             ((!baln_is_positive) && (!amt_is_positive)))
     283                 :             :     {
     284                 :           0 :         PWARN ("accounting policy gave us split that enlarges the lot!\n"
     285                 :             :                "old lot baln=%s split amt=%s lot=%s",
     286                 :             :                gnc_num_dbg_to_string (gnc_lot_get_balance(lot)),
     287                 :             :                gnc_num_dbg_to_string (split->amount),
     288                 :             :                gnc_lot_get_title (lot));
     289                 :             : 
     290                 :           0 :         acc = split->acc;
     291                 :           0 :         xaccAccountBeginEdit (acc);
     292                 :           0 :         gnc_lot_add_split (lot, split);
     293                 :           0 :         xaccAccountCommitEdit (acc);
     294                 :           0 :         return nullptr;
     295                 :             :     }
     296                 :             : 
     297                 :             :     /* If adding the split would make the lot balance change sign,
     298                 :             :      * then we split the split into two pieces: one piece that will
     299                 :             :      * bring the lot balance to zero, and another to be dealt with
     300                 :             :      * later.  */
     301                 :           7 :     cmp = gnc_numeric_compare (gnc_numeric_abs(split->amount),
     302                 :             :                                gnc_numeric_abs(baln));
     303                 :             : 
     304                 :           7 :     PINFO ("found open lot with baln=%s (%s)", gnc_num_dbg_to_string (baln),
     305                 :             :            gnc_lot_get_title (lot));
     306                 :             : 
     307                 :             :     /* cmp == -1 if amt < baln, ==0 if amt==baln */
     308                 :           7 :     if (0 >= cmp)
     309                 :             :     {
     310                 :           3 :         acc = split->acc;
     311                 :           3 :         xaccAccountBeginEdit (acc);
     312                 :           3 :         gnc_lot_add_split (lot, split);
     313                 :           3 :         PINFO ("simple added split to lot, new lot baln=%s",
     314                 :             :                gnc_num_dbg_to_string (gnc_lot_get_balance(lot)));
     315                 :           3 :         xaccAccountCommitEdit (acc);
     316                 :           3 :         return nullptr;
     317                 :             :     }
     318                 :             : 
     319                 :             :     /* If we are here, then (cmp == +1 iff (amt > baln)) and we need
     320                 :             :      * to split up the split into pieces. Do it. */
     321                 :             :     {
     322                 :           4 :         time64 now = gnc_time (nullptr), time = 0;
     323                 :             :         Split * new_split;
     324                 :             :         gnc_numeric amt_a, amt_b, amt_tot;
     325                 :             :         gnc_numeric val_a, val_b, val_tot;
     326                 :             :         gnc_numeric frac;
     327                 :             :         Transaction *trans;
     328                 :             : 
     329                 :           4 :         acc = split->acc;
     330                 :           4 :         xaccAccountBeginEdit (acc);
     331                 :           4 :         trans = split->parent;
     332                 :           4 :         xaccTransBeginEdit (trans);
     333                 :             : 
     334                 :           4 :         amt_tot = split->amount;
     335                 :           4 :         amt_a = gnc_numeric_neg (baln);
     336                 :           4 :         amt_b = gnc_numeric_sub_fixed (amt_tot, amt_a);
     337                 :           4 :         g_return_val_if_fail(gnc_numeric_check(amt_b) == GNC_ERROR_OK, nullptr);
     338                 :             : 
     339                 :           4 :         PINFO ("++++++++++++++ splitting split=%p into amt = %s + %s",
     340                 :             :                split,
     341                 :             :                gnc_num_dbg_to_string(amt_a),
     342                 :             :                gnc_num_dbg_to_string(amt_b) );
     343                 :             : 
     344                 :             :         /* Compute the value so that it holds in the same proportion:
     345                 :             :          * i.e. so that (amt_a / amt_tot) = (val_a / val_tot)
     346                 :             :          */
     347                 :           4 :         val_tot = split->value;
     348                 :           4 :         frac = gnc_numeric_div (amt_a, amt_tot,
     349                 :             :                                 GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE);
     350                 :           4 :         val_a = gnc_numeric_mul (frac, val_tot,
     351                 :             :                                  gnc_numeric_denom(val_tot),
     352                 :             :                                  GNC_HOW_RND_ROUND_HALF_UP | GNC_HOW_DENOM_EXACT);
     353                 :             : 
     354                 :           4 :         val_b = gnc_numeric_sub_fixed (val_tot, val_a);
     355                 :           4 :         if (gnc_numeric_check(val_a))
     356                 :             :         {
     357                 :           0 :             PERR("Numeric overflow\n"
     358                 :             :                  "Acct=%s Txn=%s\n"
     359                 :             :                  "\tval_tot=%s amt_a=%s amt_tot=%s\n",
     360                 :             :                  xaccAccountGetName(acc),
     361                 :             :                  xaccTransGetDescription(trans),
     362                 :             :                  gnc_num_dbg_to_string(val_tot),
     363                 :             :                  gnc_num_dbg_to_string(amt_a),
     364                 :             :                  gnc_num_dbg_to_string(amt_tot));
     365                 :             :         }
     366                 :             : 
     367                 :           4 :         if (gnc_numeric_zero_p(amt_a) || gnc_numeric_zero_p(amt_b))
     368                 :             :         {
     369                 :           0 :             PERR ("Failed to split into two!");
     370                 :             :         }
     371                 :             : 
     372                 :           4 :         PINFO ("split value is = %s = %s + %s",
     373                 :             :                gnc_num_dbg_to_string(val_tot),
     374                 :             :                gnc_num_dbg_to_string(val_a),
     375                 :             :                gnc_num_dbg_to_string(val_b) );
     376                 :             : 
     377                 :           4 :         g_return_val_if_fail (!gnc_numeric_zero_p (amt_a), nullptr);
     378                 :           4 :         g_return_val_if_fail (!gnc_numeric_check (val_a), nullptr);
     379                 :           4 :         xaccSplitSetAmount (split, amt_a);
     380                 :           4 :         xaccSplitSetValue (split, val_a);
     381                 :             : 
     382                 :             :         /* Adding this split will have the effect of closing this lot,
     383                 :             :          * because the new balance should be precisely zero. */
     384                 :           4 :         gnc_lot_add_split (lot, split);
     385                 :             : 
     386                 :             :         /* Put the remainder of the balance into a new split,
     387                 :             :          * which is in other respects just a clone of this one. */
     388                 :           4 :         new_split = xaccMallocSplit (qof_instance_get_book(acc));
     389                 :             : 
     390                 :             :         /* Copy most of the split attributes */
     391                 :           4 :         xaccSplitSetMemo (new_split, xaccSplitGetMemo (split));
     392                 :             :         /* Set split-action with gnc_set_num_action which is the same as
     393                 :             :          * xaccSplitSetAction with these arguments; use gnc_get_num_action to get
     394                 :             :          * split-action which is the same as xaccSplitGetAction */
     395                 :           4 :         gnc_set_num_action(nullptr, new_split, nullptr, gnc_get_num_action(nullptr, split));
     396                 :           4 :         xaccSplitSetReconcile (new_split, xaccSplitGetReconcile (split));
     397                 :           4 :         time = xaccSplitGetDateReconciled (split);
     398                 :           4 :         xaccSplitSetDateReconciledSecs (new_split, time);
     399                 :             : 
     400                 :             :         /* Set the lot-split and peer_guid properties on the two
     401                 :             :          * splits to indicate that they're linked. 
     402                 :             :          */
     403                 :           4 :         xaccSplitAddPeerSplit(split, new_split, now);
     404                 :           4 :         xaccSplitAddPeerSplit(new_split, split, now);
     405                 :           4 :         xaccAccountInsertSplit (acc, new_split);
     406                 :           4 :         xaccTransAppendSplit (trans, new_split);
     407                 :             :         /* Set the amount and value after the split is in the transaction
     408                 :             :            so it can find the correct denominator to use.  Otherwise it
     409                 :             :            uses 100000 which may cause an overflow in some of the tests
     410                 :             :            in test-period */
     411                 :           4 :         xaccSplitSetAmount (new_split, amt_b);
     412                 :           4 :         xaccSplitSetValue (new_split, val_b);
     413                 :           4 :         xaccTransCommitEdit (trans);
     414                 :           4 :         xaccAccountCommitEdit (acc);
     415                 :           4 :         return new_split;
     416                 :             :     }
     417                 :             : }
     418                 :             : 
     419                 :             : /* ============================================================== */
     420                 :             : 
     421                 :             : /* Accounting-policy callback.  Given an account and an amount,
     422                 :             :  * this routine should return a lot.  By implementing this as
     423                 :             :  * a callback, we can 'easily' add other accounting policies.
     424                 :             :  */
     425                 :             : gboolean
     426                 :          56 : xaccSplitAssign (Split *split)
     427                 :             : {
     428                 :             :     Account *acc;
     429                 :          56 :     gboolean splits_split_up = FALSE;
     430                 :             :     GNCLot *lot;
     431                 :             :     GNCPolicy *pcy;
     432                 :             : 
     433                 :          56 :     if (!split) return FALSE;
     434                 :             : 
     435                 :             :     /* If this split already belongs to a lot or the account doesn't
     436                 :             :      * have lots, we are done.
     437                 :             :      */
     438                 :          56 :     if (split->lot) return FALSE;
     439                 :          56 :     g_return_val_if_fail (split->gains == GAINS_STATUS_UNKNOWN ||
     440                 :             :                           (split->gains & GAINS_STATUS_GAINS) == FALSE, FALSE);
     441                 :          56 :     acc = split->acc;
     442                 :          56 :     if (!xaccAccountHasTrades (acc))
     443                 :           0 :         return FALSE;
     444                 :          56 :     if (gnc_numeric_zero_p (split->amount))
     445                 :           0 :         return FALSE;
     446                 :             : 
     447                 :          56 :     ENTER ("(split=%p)", split);
     448                 :             : 
     449                 :          56 :     pcy = gnc_account_get_policy(acc);
     450                 :          56 :     xaccAccountBeginEdit (acc);
     451                 :             : 
     452                 :             :     /* If we are here, this split does not belong to any lot.
     453                 :             :      * We ask the policy for a lot to assign it to.  This
     454                 :             :      * block is written in the form of a while loop, since we
     455                 :             :      * may have to bust a split across several lots.
     456                 :             :      */
     457                 :         116 :     while (split)
     458                 :             :     {
     459                 :          60 :         PINFO ("have split %p amount=%s", split,
     460                 :             :                gnc_num_dbg_to_string (split->amount));
     461                 :          60 :         split->gains |= GAINS_STATUS_VDIRTY;
     462                 :          60 :         lot = pcy->PolicyGetLot (pcy, split);
     463                 :          60 :         if (!lot)
     464                 :             :         {
     465                 :          53 :             lot = gnc_lot_make_default (acc);
     466                 :          53 :             PINFO ("start new lot (%s)", gnc_lot_get_title(lot));
     467                 :             :         }
     468                 :          60 :         split = xaccSplitAssignToLot (split, lot);
     469                 :          60 :         if (split) splits_split_up = TRUE;
     470                 :             :     }
     471                 :          56 :     xaccAccountCommitEdit (acc);
     472                 :             : 
     473                 :          56 :     LEAVE (" split_up=%d", splits_split_up);
     474                 :          56 :     return splits_split_up;
     475                 :             : }
     476                 :             : 
     477                 :             : /* ============================================================== */
     478                 :             : 
     479                 :             : Split *
     480                 :         290 : xaccSplitGetCapGainsSplit (const Split *split)
     481                 :             : {
     482                 :             :     GncGUID *gains_guid;
     483                 :             :     Split *gains_split;
     484                 :             : 
     485                 :         290 :     if (!split) return nullptr;
     486                 :             : 
     487                 :         290 :     qof_instance_get (QOF_INSTANCE (split),
     488                 :             :                       "gains-split", &gains_guid,
     489                 :             :                       nullptr);
     490                 :         290 :     if (!gains_guid) return nullptr;
     491                 :             : 
     492                 :             :     /* Both splits will be in the same collection, so search there. */
     493                 :           0 :     gains_split = (Split*) qof_collection_lookup_entity (
     494                 :           0 :                       qof_instance_get_collection(split), gains_guid);
     495                 :           0 :     PINFO ("split=%p has gains-split=%p", split, gains_split);
     496                 :           0 :     guid_free (gains_guid);
     497                 :           0 :     return gains_split;
     498                 :             : }
     499                 :             : 
     500                 :             : /* ============================================================== */
     501                 :             : 
     502                 :             : Split *
     503                 :          96 : xaccSplitGetGainsSourceSplit (const Split *split)
     504                 :             : {
     505                 :             :     GncGUID *source_guid;
     506                 :             :     Split *source_split;
     507                 :             : 
     508                 :          96 :     if (!split) return nullptr;
     509                 :             : 
     510                 :          96 :     qof_instance_get (QOF_INSTANCE (split),
     511                 :             :                       "gains-source", &source_guid,
     512                 :             :                       nullptr);
     513                 :          96 :     if (!source_guid) return nullptr;
     514                 :             : 
     515                 :             :     /* Both splits will be in the same collection, so search there. */
     516                 :          54 :     source_split = (Split*) qof_collection_lookup_entity(
     517                 :          18 :                        qof_instance_get_collection(split), source_guid);
     518                 :          18 :     PINFO ("split=%p has source-split=%p", split, source_split);
     519                 :          18 :     guid_free (source_guid);
     520                 :          18 :     return source_split;
     521                 :             : }
     522                 :             : 
     523                 :             : /* ============================================================== */
     524                 :             : 
     525                 :             : void
     526                 :         130 : xaccSplitComputeCapGains(Split *split, Account *gain_acc)
     527                 :             : {
     528                 :             :     SplitList *node;
     529                 :             :     GNCLot *lot;
     530                 :             :     GNCPolicy *pcy;
     531                 :         130 :     gnc_commodity *currency = nullptr;
     532                 :         130 :     gnc_numeric zero = gnc_numeric_zero();
     533                 :             :     gnc_numeric value;
     534                 :             :     gnc_numeric frac;
     535                 :             :     gnc_numeric opening_amount, opening_value;
     536                 :             :     gnc_numeric lot_amount, lot_value;
     537                 :             :     gnc_commodity *opening_currency;
     538                 :             : 
     539                 :         234 :     if (!split) return;
     540                 :         130 :     lot = split->lot;
     541                 :         130 :     if (!lot) return;
     542                 :         130 :     pcy = gnc_account_get_policy(gnc_lot_get_account(lot));
     543                 :         130 :     currency = split->parent->common_currency;
     544                 :             : 
     545                 :         130 :     ENTER ("(split=%p gains=%p status=0x%x lot=%s)", split,
     546                 :             :            split->gains_split, split->gains, gnc_lot_get_title(lot));
     547                 :             : 
     548                 :             :     /* Make sure the status flags and pointers are initialized */
     549                 :         130 :     xaccSplitDetermineGainStatus(split);
     550                 :             : 
     551                 :             :     /* Not possible to have gains if the transaction currency and
     552                 :             :      * account commodity are identical. */
     553                 :         130 :     if (gnc_commodity_equal (currency,
     554                 :         130 :                              xaccAccountGetCommodity(split->acc)))
     555                 :             :     {
     556                 :           0 :         LEAVE ("Currency transfer, gains not possible, returning.");
     557                 :           0 :         return;
     558                 :             :     }
     559                 :             : 
     560                 :         130 :     if (pcy->PolicyIsOpeningSplit (pcy, lot, split))
     561                 :             :     {
     562                 :             : #if MOVE_THIS_TO_A_DATA_INTEGRITY_SCRUBBER
     563                 :             :         /* Check to make sure that this opening split doesn't
     564                 :             :          * have a cap-gain transaction associated with it.
     565                 :             :          * If it does, that's wrong, and we ruthlessly destroy it.
     566                 :             :          * XXX Don't do this, it leads to infinite loops.
     567                 :             :          * We need to scrub out errors like this elsewhere!
     568                 :             :          */
     569                 :             :         if (xaccSplitGetCapGainsSplit (split))
     570                 :             :         {
     571                 :             :             Split *gains_split = xaccSplitGetCapGainsSplit(split);
     572                 :             :             Transaction *trans = gains_split->parent;
     573                 :             :             PERR ("Opening Split must not have cap gains!!\n");
     574                 :             : 
     575                 :             :             xaccTransBeginEdit (trans);
     576                 :             :             xaccTransDestroy (trans);
     577                 :             :             xaccTransCommitEdit (trans);
     578                 :             :         }
     579                 :             : #endif
     580                 :         104 :         LEAVE ("Lot opening split, returning.");
     581                 :         104 :         return;
     582                 :             :     }
     583                 :             : 
     584                 :          26 :     if (g_strcmp0 ("stock-split", xaccSplitGetType (split)) == 0)
     585                 :             :     {
     586                 :           0 :         LEAVE ("Stock split split, returning.");
     587                 :           0 :         return;
     588                 :             :     }
     589                 :             : 
     590                 :          26 :     if (GAINS_STATUS_GAINS & split->gains)
     591                 :             :     {
     592                 :             :         Split *s;
     593                 :          12 :         PINFO ("split is a gains recording split, switch over");
     594                 :             :         /* If this is the split that records the gains, then work with
     595                 :             :          * the split that generates the gains.
     596                 :             :          */
     597                 :             :         /* split = xaccSplitGetCapGainsSplit (split); */
     598                 :          12 :         s = split->gains_split;
     599                 :             : 
     600                 :             :         /* This should never be nullptr, and if it is, and its matching
     601                 :             :          * parent can't be found, then its a bug, and we should be
     602                 :             :          * discarding this split.   But ... for now .. return.
     603                 :             :          * XXX move appropriate actions to a 'scrub' routine'
     604                 :             :          */
     605                 :          12 :         if (!s)
     606                 :             :         {
     607                 :           0 :             PERR ("Bad gains-split pointer! .. trying to recover.");
     608                 :           0 :             split->gains_split = xaccSplitGetCapGainsSplit (split);
     609                 :           0 :             s = split->gains_split;
     610                 :           0 :             if (!s) return;
     611                 :             : #if MOVE_THIS_TO_A_DATA_INTEGRITY_SCRUBBER
     612                 :             :             xaccTransDestroy (trans);
     613                 :             : #endif
     614                 :             :         }
     615                 :          12 :         split = s;
     616                 :             :     }
     617                 :             : 
     618                 :             :     /* Note: if the value of the 'opening' split(s) has changed,
     619                 :             :      * then the cap gains are changed. So we need to check not
     620                 :             :      * only if this split is dirty, but also the lot-opening splits. */
     621                 :          26 :     for (node = gnc_lot_get_split_list(lot); node; node = node->next)
     622                 :             :     {
     623                 :          26 :         Split *s = GNC_SPLIT(node->data);
     624                 :          26 :         if (pcy->PolicyIsOpeningSplit(pcy, lot, s))
     625                 :             :         {
     626                 :          26 :             if (GAINS_STATUS_UNKNOWN == s->gains) xaccSplitDetermineGainStatus (s);
     627                 :          26 :             if (s->gains & GAINS_STATUS_VDIRTY)
     628                 :             :             {
     629                 :             :                 /* Force a recompute to occur */
     630                 :          26 :                 split->gains |= GAINS_STATUS_VDIRTY;
     631                 :          26 :                 break;
     632                 :             :             }
     633                 :             :         }
     634                 :             :     }
     635                 :             : 
     636                 :             :     /* If it doesn't look like this split is 'dirty', then there's
     637                 :             :      * nothing to do. Just return. */
     638                 :          26 :     if ((FALSE == (split->gains & GAINS_STATUS_A_VDIRTY))  &&
     639                 :           0 :             (split->gains_split) &&
     640                 :           0 :             (FALSE == (split->gains_split->gains & GAINS_STATUS_A_VDIRTY)))
     641                 :             :     {
     642                 :           0 :         LEAVE ("split not dirty, returning");
     643                 :           0 :         return;
     644                 :             :     }
     645                 :             : 
     646                 :             :     /* Yow! If amount is zero, there's nothing to do! Amount-zero splits
     647                 :             :      * may exist if users attempted to manually record gains. */
     648                 :          26 :     if (gnc_numeric_zero_p (split->amount)) return;
     649                 :             : 
     650                 :             :     /* If we got to here, then the split or something related is
     651                 :             :      * 'dirty' and the gains really do need to be recomputed.
     652                 :             :      * So start working things. */
     653                 :             : 
     654                 :             :     /* Get the amount and value in this lot at the time of this transaction. */
     655                 :          26 :     gnc_lot_get_balance_before (lot, split, &lot_amount, &lot_value);
     656                 :             : 
     657                 :          26 :     pcy->PolicyGetLotOpening (pcy, lot, &opening_amount, &opening_value,
     658                 :             :                               &opening_currency);
     659                 :             : 
     660                 :             :     /* Check to make sure the lot-opening currency and this split
     661                 :             :      * use the same currency */
     662                 :          26 :     if (FALSE == gnc_commodity_equiv (currency, opening_currency))
     663                 :             :     {
     664                 :             :         /* OK, the purchase and the sale were made in different currencies.
     665                 :             :          * I don't know how to compute cap gains for that.  This is not
     666                 :             :          * an error. Just punt, silently.
     667                 :             :          */
     668                 :           0 :         LEAVE ("Can't compute gains, mismatched commodities!");
     669                 :           0 :         return;
     670                 :             :     }
     671                 :             : 
     672                 :             :     /* Opening amount should be larger (or equal) to current split,
     673                 :             :      * and it should be of the opposite sign.
     674                 :             :      * XXX This should really be a part of a scrub routine that
     675                 :             :      * cleans up the lot, before we get at it!
     676                 :             :      */
     677                 :          26 :     if (0 > gnc_numeric_compare (gnc_numeric_abs(lot_amount),
     678                 :             :                                  gnc_numeric_abs(split->amount)))
     679                 :             :     {
     680                 :             :         GList *n;
     681                 :           0 :         for (n = gnc_lot_get_split_list(lot); n; n = n->next)
     682                 :             :         {
     683                 :           0 :             Split *s = GNC_SPLIT(n->data);
     684                 :           0 :             PINFO ("split amt=%s", gnc_num_dbg_to_string(s->amount));
     685                 :             :         }
     686                 :           0 :         PERR ("Malformed Lot \"%s\"! (too thin!) "
     687                 :             :               "opening amt=%s split amt=%s baln=%s",
     688                 :             :               gnc_lot_get_title (lot),
     689                 :             :               gnc_num_dbg_to_string (lot_amount),
     690                 :             :               gnc_num_dbg_to_string (split->amount),
     691                 :             :               gnc_num_dbg_to_string (gnc_lot_get_balance(lot)));
     692                 :           0 :         return;
     693                 :             :     }
     694                 :          26 :     if ( (gnc_numeric_negative_p(lot_amount) ||
     695                 :          36 :             gnc_numeric_positive_p(split->amount)) &&
     696                 :          10 :             (gnc_numeric_positive_p(lot_amount) ||
     697                 :          10 :              gnc_numeric_negative_p(split->amount)))
     698                 :             :     {
     699                 :             :         GList *n;
     700                 :           0 :         for (n = gnc_lot_get_split_list(lot); n; n = n->next)
     701                 :             :         {
     702                 :           0 :             Split *s = GNC_SPLIT(n->data);
     703                 :           0 :             PINFO ("split amt=%s", gnc_num_dbg_to_string(s->amount));
     704                 :             :         }
     705                 :           0 :         PERR ("Malformed Lot \"%s\"! (too fat!) "
     706                 :             :               "opening amt=%s split amt=%s baln=%s",
     707                 :             :               gnc_lot_get_title (lot),
     708                 :             :               gnc_num_dbg_to_string (lot_amount),
     709                 :             :               gnc_num_dbg_to_string (split->amount),
     710                 :             :               gnc_num_dbg_to_string (gnc_lot_get_balance(lot)));
     711                 :           0 :         return;
     712                 :             :     }
     713                 :             : 
     714                 :             :     /* The cap gains is the difference between the basis prior to the
     715                 :             :      * current split, and the current split, pro-rated for an equal
     716                 :             :      * amount of shares.
     717                 :             :      * i.e. purchase_price = lot_value / lot_amount
     718                 :             :      * cost_basis = purchase_price * current_split_amount
     719                 :             :      * cap_gain = current_split_value - cost_basis
     720                 :             :      */
     721                 :             :     /* Fraction of the lot that this split represents: */
     722                 :          26 :     frac = gnc_numeric_div (split->amount, lot_amount,
     723                 :             :                             GNC_DENOM_AUTO,
     724                 :             :                             GNC_HOW_DENOM_REDUCE);
     725                 :             :     /* Basis for this split: */
     726                 :          26 :     value = gnc_numeric_mul (frac, lot_value,
     727                 :             :                              gnc_numeric_denom(opening_value),
     728                 :             :                              GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
     729                 :             :     /* Capital gain for this split: */
     730                 :          26 :     value = gnc_numeric_sub (value, split->value,
     731                 :             :                              GNC_DENOM_AUTO, GNC_HOW_DENOM_FIXED);
     732                 :          26 :     PINFO ("Open amt=%s val=%s;  split amt=%s val=%s; gains=%s\n",
     733                 :             :            gnc_num_dbg_to_string (lot_amount),
     734                 :             :            gnc_num_dbg_to_string (lot_value),
     735                 :             :            gnc_num_dbg_to_string (split->amount),
     736                 :             :            gnc_num_dbg_to_string (split->value),
     737                 :             :            gnc_num_dbg_to_string (value));
     738                 :          26 :     if (gnc_numeric_check (value))
     739                 :             :     {
     740                 :           0 :         PERR ("Numeric overflow during gains calculation\n"
     741                 :             :               "Acct=%s Txn=%s\n"
     742                 :             :               "\tOpen amt=%s val=%s\n\tsplit amt=%s val=%s\n\tgains=%s\n",
     743                 :             :               xaccAccountGetName(split->acc),
     744                 :             :               xaccTransGetDescription(split->parent),
     745                 :             :               gnc_num_dbg_to_string (lot_amount),
     746                 :             :               gnc_num_dbg_to_string (lot_value),
     747                 :             :               gnc_num_dbg_to_string (split->amount),
     748                 :             :               gnc_num_dbg_to_string (split->value),
     749                 :             :               gnc_num_dbg_to_string (value));
     750                 :           0 :         return;
     751                 :             :     }
     752                 :             : 
     753                 :             :     /* Are the cap gains zero?  If not, add a balancing transaction.
     754                 :             :      * As per design doc lots.txt: the transaction has two splits,
     755                 :             :      * with equal & opposite values.  The amt of one iz zero (so as
     756                 :             :      * not to upset the lot balance), the amt of the other is the same
     757                 :             :      * as its value (its the realized gain/loss).
     758                 :             :      */
     759                 :          26 :     if (FALSE == gnc_numeric_zero_p (value))
     760                 :             :     {
     761                 :             :         Transaction *trans;
     762                 :             :         Split *lot_split, *gain_split;
     763                 :             :         gboolean new_gain_split;
     764                 :          24 :         gnc_numeric negvalue = gnc_numeric_neg (value);
     765                 :             : 
     766                 :             :         /* See if there already is an associated gains transaction.
     767                 :             :          * If there is, adjust its value as appropriate. Else, create
     768                 :             :          * a new gains transaction.
     769                 :             :          */
     770                 :             :         /* lot_split = xaccSplitGetCapGainsSplit (split);  */
     771                 :          24 :         lot_split = split->gains_split;
     772                 :             : 
     773                 :          24 :         if (nullptr == lot_split)
     774                 :             :         {
     775                 :           6 :             Account *lot_acc = gnc_lot_get_account(lot);
     776                 :           6 :             QofBook *book = qof_instance_get_book(lot_acc);
     777                 :           6 :             Transaction *base_txn = xaccSplitGetParent (split);
     778                 :             : 
     779                 :           6 :             new_gain_split = TRUE;
     780                 :             : 
     781                 :           6 :             lot_split = xaccMallocSplit (book);
     782                 :           6 :             gain_split = xaccMallocSplit (book);
     783                 :             : 
     784                 :             :             /* Check to make sure the gains account currency matches. */
     785                 :           6 :             if ((nullptr == gain_acc) ||
     786                 :           0 :                     (FALSE == gnc_commodity_equiv (currency,
     787                 :           0 :                                                    xaccAccountGetCommodity(gain_acc))))
     788                 :             :             {
     789                 :           6 :                 gain_acc = xaccAccountGainsAccount (lot_acc, currency);
     790                 :             :             }
     791                 :             : 
     792                 :           6 :             xaccAccountBeginEdit (gain_acc);
     793                 :           6 :             xaccAccountInsertSplit (gain_acc, gain_split);
     794                 :           6 :             xaccAccountCommitEdit (gain_acc);
     795                 :             : 
     796                 :           6 :             xaccAccountBeginEdit (lot_acc);
     797                 :           6 :             xaccAccountInsertSplit (lot_acc, lot_split);
     798                 :           6 :             xaccAccountCommitEdit (lot_acc);
     799                 :             : 
     800                 :           6 :             trans = xaccMallocTransaction (book);
     801                 :             : 
     802                 :           6 :             xaccTransBeginEdit (trans);
     803                 :           6 :             xaccTransSetCurrency (trans, currency);
     804                 :           6 :             xaccTransSetDescription (trans, _("Realized Gain/Loss"));
     805                 :             : 
     806                 :           6 :             xaccTransAppendSplit (trans, lot_split);
     807                 :           6 :             xaccTransAppendSplit (trans, gain_split);
     808                 :             : 
     809                 :           6 :             xaccSplitSetMemo (lot_split, _("Realized Gain/Loss"));
     810                 :           6 :             xaccSplitSetMemo (gain_split, _("Realized Gain/Loss"));
     811                 :             : 
     812                 :             :             /* For the new transaction, set the split properties indicating
     813                 :             :              * that this is the gains transaction that corresponds
     814                 :             :              * to the gains source.
     815                 :             :              */
     816                 :           6 :             xaccTransBeginEdit (base_txn);
     817                 :           6 :             qof_instance_set (QOF_INSTANCE (split),
     818                 :           6 :                               "gains-split", xaccSplitGetGUID (lot_split),
     819                 :             :                               nullptr);
     820                 :           6 :             xaccTransCommitEdit (base_txn);
     821                 :           6 :             qof_instance_set (QOF_INSTANCE (lot_split),
     822                 :           6 :                               "gains-source", xaccSplitGetGUID (split),
     823                 :             :                               nullptr);
     824                 :             : 
     825                 :             :         }
     826                 :             :         else
     827                 :             :         {
     828                 :          18 :             trans = lot_split->parent;
     829                 :          18 :             gain_split = xaccSplitGetOtherSplit (lot_split);
     830                 :             : 
     831                 :             :             /* If the gains transaction has been edited so that it no longer has
     832                 :             :                just two splits, ignore it and assume it's still correct. */
     833                 :          18 :             if (!gain_split)
     834                 :             :             {
     835                 :           0 :                 new_gain_split = FALSE;
     836                 :             :             }
     837                 :             :             /* If the gain is already recorded correctly do nothing.  This is
     838                 :             :              * more than just an optimization since this may be called during
     839                 :             :              * gnc_book_partition_txn and depending on the order in which things
     840                 :             :              * happen some splits may be in the wrong book at that time. */
     841                 :          54 :             else if (split->gains_split == lot_split &&
     842                 :          18 :                      lot_split->gains_split == split &&
     843                 :          18 :                      gain_split->gains_split == split &&
     844                 :          18 :                      gnc_numeric_equal (xaccSplitGetValue (lot_split), value) &&
     845                 :          18 :                      gnc_numeric_zero_p (xaccSplitGetAmount (lot_split)) &&
     846                 :          54 :                      gnc_numeric_equal (xaccSplitGetValue (gain_split), negvalue) &&
     847                 :          18 :                      gnc_numeric_equal (xaccSplitGetAmount (gain_split), negvalue))
     848                 :             :             {
     849                 :          18 :                 new_gain_split = FALSE;
     850                 :             :             }
     851                 :             :             else
     852                 :             :             {
     853                 :           0 :                 new_gain_split = TRUE;
     854                 :           0 :                 xaccTransBeginEdit (trans);
     855                 :             : 
     856                 :             :                 /* Make sure the existing gains trans has the correct currency,
     857                 :             :                  * just in case someone screwed with it! */
     858                 :           0 :                 if (FALSE == gnc_commodity_equiv(currency, trans->common_currency))
     859                 :             :                 {
     860                 :           0 :                     PWARN ("Resetting the transaction currency!");
     861                 :           0 :                     xaccTransSetCurrency (trans, currency);
     862                 :             :                 }
     863                 :             :             }
     864                 :             :         }
     865                 :             : 
     866                 :          24 :         if (new_gain_split)
     867                 :             :         {
     868                 :             :             /* Common to both */
     869                 :           6 :             time64 time = xaccTransRetDatePosted (split->parent);
     870                 :           6 :             xaccTransSetDatePostedSecs (trans, time);
     871                 :           6 :             xaccTransSetDateEnteredSecs (trans, gnc_time (nullptr));
     872                 :             : 
     873                 :           6 :             xaccSplitSetAmount (lot_split, zero);
     874                 :           6 :             xaccSplitSetValue (lot_split, value);
     875                 :             : 
     876                 :           6 :             xaccSplitSetAmount (gain_split, negvalue);
     877                 :           6 :             xaccSplitSetValue (gain_split, negvalue);
     878                 :             : 
     879                 :             :             /* Some short-cuts to help avoid the above property lookup. */
     880                 :           6 :             split->gains = GAINS_STATUS_CLEAN;
     881                 :           6 :             split->gains_split = lot_split;
     882                 :           6 :             lot_split->gains = GAINS_STATUS_GAINS;
     883                 :           6 :             lot_split->gains_split = split;
     884                 :           6 :             gain_split->gains = GAINS_STATUS_GAINS;
     885                 :           6 :             gain_split->gains_split = split;
     886                 :             : 
     887                 :             :             /* Do this last since it may generate an event that will call us
     888                 :             :                recursively. */
     889                 :           6 :             gnc_lot_add_split (lot, lot_split);
     890                 :             : 
     891                 :           6 :             xaccTransCommitEdit (trans);
     892                 :             :         }
     893                 :             :     }
     894                 :          26 :     LEAVE ("(lot=%s)", gnc_lot_get_title(lot));
     895                 :             : }
     896                 :             : 
     897                 :             : /* ============================================================== */
     898                 :             : 
     899                 :             : gnc_numeric
     900                 :           0 : xaccSplitGetCapGains(Split * split)
     901                 :             : {
     902                 :           0 :     if (!split) return gnc_numeric_zero();
     903                 :           0 :     ENTER("(split=%p)", split);
     904                 :             : 
     905                 :           0 :     if (GAINS_STATUS_UNKNOWN == split->gains)
     906                 :           0 :         xaccSplitDetermineGainStatus(split);
     907                 :           0 :     if ((split->gains & GAINS_STATUS_A_VDIRTY) ||
     908                 :           0 :             (split->gains_split &&
     909                 :           0 :              (split->gains_split->gains & GAINS_STATUS_A_VDIRTY)))
     910                 :             :     {
     911                 :           0 :         xaccSplitComputeCapGains (split, nullptr);
     912                 :             :     }
     913                 :             : 
     914                 :             :     /* If this is the source split, get the gains from the one
     915                 :             :      * that records the gains.  If this already is the gains split,
     916                 :             :      * its a no-op. */
     917                 :           0 :     if (!(GAINS_STATUS_GAINS & split->gains))
     918                 :             :     {
     919                 :             :         /* split = xaccSplitGetCapGainsSplit (split); */
     920                 :           0 :         split = split->gains_split;
     921                 :             :     }
     922                 :             : 
     923                 :           0 :     LEAVE("(split=%p)", split);
     924                 :           0 :     if (!split) return gnc_numeric_zero();
     925                 :             : 
     926                 :           0 :     return split->value;
     927                 :             : }
     928                 :             : 
     929                 :             : /* ============================================================== */
     930                 :             : 
     931                 :             : void
     932                 :          52 : xaccLotComputeCapGains (GNCLot *lot, Account *gain_acc)
     933                 :             : {
     934                 :             :     SplitList *node;
     935                 :             :     GNCPolicy *pcy;
     936                 :          52 :     gboolean is_dirty = FALSE;
     937                 :             : 
     938                 :             :     /* Note: if the value of the 'opening' split(s) has changed,
     939                 :             :      * then the cap gains are changed. To capture this, we need
     940                 :             :      * to mark all splits dirty if the opening splits are dirty. */
     941                 :             : 
     942                 :          52 :     ENTER("(lot=%p)", lot);
     943                 :          52 :     pcy = gnc_account_get_policy(gnc_lot_get_account(lot));
     944                 :         111 :     for (node = gnc_lot_get_split_list(lot); node; node = node->next)
     945                 :             :     {
     946                 :          59 :         Split *s = GNC_SPLIT(node->data);
     947                 :          59 :         if (pcy->PolicyIsOpeningSplit(pcy, lot, s))
     948                 :             :         {
     949                 :          52 :             if (GAINS_STATUS_UNKNOWN == s->gains)
     950                 :          52 :                 xaccSplitDetermineGainStatus(s);
     951                 :          52 :             if (s->gains & GAINS_STATUS_VDIRTY)
     952                 :             :             {
     953                 :          52 :                 is_dirty = TRUE;
     954                 :          52 :                 s->gains &= ~GAINS_STATUS_VDIRTY;
     955                 :             :             }
     956                 :             :         }
     957                 :             :     }
     958                 :             : 
     959                 :          52 :     if (is_dirty)
     960                 :             :     {
     961                 :         111 :         for (node = gnc_lot_get_split_list(lot); node; node = node->next)
     962                 :             :         {
     963                 :          59 :             Split *s = GNC_SPLIT(node->data);
     964                 :          59 :             s->gains |= GAINS_STATUS_VDIRTY;
     965                 :             :         }
     966                 :             :     }
     967                 :             : 
     968                 :         117 :     for (node = gnc_lot_get_split_list(lot); node; node = node->next)
     969                 :             :     {
     970                 :          65 :         Split *s = GNC_SPLIT(node->data);
     971                 :          65 :         xaccSplitComputeCapGains (s, gain_acc);
     972                 :             :     }
     973                 :          52 :     LEAVE("(lot=%p)", lot);
     974                 :          52 : }
     975                 :             : 
     976                 :             : /* =========================== END OF FILE ======================= */
        

Generated by: LCOV version 2.0-1