LCOV - code coverage report
Current view: top level - libgnucash/engine - Scrub3.cpp (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 89.8 % 59 53
Test Date: 2026-11-02 18:19:43 Functions: 100.0 % 5 5
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /********************************************************************\
       2              :  * Scrub3.c -- Constrain Cap Gains to Track Sources of Gains        *
       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 Scrub3.c
      23              :  *  @brief Constrain Cap Gains to Track Sources of Gains
      24              :  *  @author Created by Linas Vepstas Sept 2003
      25              :  *  @author Copyright (c) 2003,2004 Linas Vepstas <linas@linas.org>
      26              :  *
      27              :  * Provides a set of functions and utilities for checking and
      28              :  * repairing ('scrubbing clean') the usage of Cap Gains
      29              :  * transactions in stock and commodity accounts.
      30              :  */
      31              : 
      32              : #include <config.h>
      33              : 
      34              : #include <glib.h>
      35              : 
      36              : #include "cap-gains.h"
      37              : #include "gnc-commodity.h"
      38              : #include "gnc-engine.h"
      39              : #include "gnc-lot.h"
      40              : #include "policy-p.h"
      41              : #include "Account.h"
      42              : #include "AccountP.hpp"
      43              : #include "Scrub2.h"
      44              : #include "Scrub3.h"
      45              : #include "Transaction.h"
      46              : #include "TransactionP.hpp"
      47              : 
      48              : static QofLogModule log_module = GNC_MOD_LOT;
      49              : 
      50              : /* ================================================================= */
      51              : /** Cap gains are possible only if the lot commodity is not the same
      52              :  * as the transaction currency.  We assume here that all splits in
      53              :  * the lot share the same transaction currency, and so we look at
      54              :  * the first split, and see what it's currency is.
      55              :  * This routine returns TRUE if cap gains are possible.
      56              :  */
      57              : 
      58              : static inline gboolean
      59           55 : gains_possible (GNCLot *lot)
      60              : {
      61              :     SplitList *node;
      62              :     Account *acc;
      63              :     Split *split;
      64              :     gboolean comeq;
      65              :     gnc_commodity *acc_commodity;
      66              : 
      67           55 :     acc = gnc_lot_get_account (lot);
      68              : 
      69           55 :     node = gnc_lot_get_split_list (lot);
      70           55 :     if (!node) return FALSE;
      71           55 :     split = GNC_SPLIT(node->data);
      72              : 
      73           55 :     acc_commodity = xaccAccountGetCommodity(acc);
      74           55 :     comeq = gnc_commodity_equiv (acc_commodity, split->parent->common_currency);
      75           55 :     return (FALSE == comeq);
      76              : }
      77              : 
      78              : /* ================================================================= */
      79              : /* XXX What happens if, as a result of scrubbing, the lot is empty?
      80              :  * I don't think this is handled properly.  I think that what will
      81              :  * happen is we'll end up with an empty, closed lot ... ?
      82              :  */
      83              : 
      84              : gboolean
      85           55 : xaccScrubLot (GNCLot *lot)
      86              : {
      87           55 :     gboolean splits_deleted = FALSE;
      88              :     gnc_numeric lot_baln;
      89              :     gboolean opening_baln_is_pos, lot_baln_is_pos;
      90              :     Account *acc;
      91              :     GNCPolicy *pcy;
      92              : 
      93           55 :     if (!lot) return FALSE;
      94           55 :     ENTER ("(lot=%p) %s", lot, gnc_lot_get_title(lot));
      95              : 
      96           55 :     acc = gnc_lot_get_account (lot);
      97           55 :     pcy = gnc_account_get_policy(acc);
      98           55 :     xaccAccountBeginEdit(acc);
      99           55 :     xaccScrubMergeLotSubSplits (lot, TRUE);
     100              : 
     101              :     /* If the lot balance is zero, we don't need to rebalance */
     102           55 :     lot_baln = gnc_lot_get_balance (lot);
     103           55 :     PINFO ("lot baln=%s for %s", gnc_num_dbg_to_string (lot_baln),
     104              :            gnc_lot_get_title(lot));
     105           55 :     if (! gnc_numeric_zero_p (lot_baln))
     106              :     {
     107              :         SplitList *node;
     108              :         gnc_numeric opening_baln;
     109              : 
     110              :         /* Get the opening balance for this lot */
     111           48 :         pcy->PolicyGetLotOpening (pcy, lot, &opening_baln, nullptr, nullptr);
     112           48 :         PINFO ("lot opener baln=%s", gnc_num_dbg_to_string (opening_baln));
     113              : 
     114              :         /* If the lot is fat, give the boot to all the non-opening
     115              :          * splits, and refill it */
     116           48 :         opening_baln_is_pos = gnc_numeric_positive_p(opening_baln);
     117           48 :         lot_baln_is_pos = gnc_numeric_positive_p(lot_baln);
     118           48 :         if ((opening_baln_is_pos || lot_baln_is_pos) &&
     119           21 :                 ((!opening_baln_is_pos) || (!lot_baln_is_pos)))
     120              :         {
     121            0 : rethin:
     122            0 :             for (node = gnc_lot_get_split_list(lot); node; node = node->next)
     123              :             {
     124            0 :                 Split *s = GNC_SPLIT(node->data);
     125            0 :                 if (pcy->PolicyIsOpeningSplit (pcy, lot, s)) continue;
     126            0 :                 gnc_lot_remove_split (lot, s);
     127            0 :                 goto rethin;
     128              :             }
     129              :         }
     130              : 
     131              :         /* At this point the lot is thin, so try to fill it */
     132           48 :         xaccLotFill (lot);
     133              : 
     134              :         /* Make sure there are no subsplits. */
     135           48 :         splits_deleted = xaccScrubMergeLotSubSplits (lot, TRUE);
     136              :     }
     137              : 
     138              :     /* Now re-compute cap gains, and then double-check that.
     139              :      * But we only compute cap-gains if gains are possible;
     140              :      * that is if the lot commodity is not the same as the
     141              :      * currency. That is, one can't possibly have gains
     142              :      * selling dollars for dollars.  The business modules
     143              :      * use lots with lot commodity == lot currency.
     144              :      */
     145           55 :     if (gains_possible (lot))
     146              :     {
     147           54 :         xaccLotComputeCapGains (lot, nullptr);
     148           54 :         xaccLotScrubDoubleBalance (lot);
     149              :     }
     150           55 :     xaccAccountCommitEdit(acc);
     151              : 
     152           55 :     LEAVE ("(lot=%s, deleted=%d)", gnc_lot_get_title(lot), splits_deleted);
     153           55 :     return splits_deleted;
     154              : }
     155              : 
     156              : /* ============================================================== */
     157              : 
     158              : void
     159            6 : xaccAccountScrubLots (Account *acc)
     160              : {
     161              :     LotList *lots, *node;
     162            6 :     if (!acc) return;
     163            6 :     if (FALSE == xaccAccountHasTrades (acc)) return;
     164              : 
     165            5 :     ENTER ("(acc=%s)", xaccAccountGetName(acc));
     166            5 :     xaccAccountBeginEdit(acc);
     167            5 :     xaccAccountAssignLots (acc);
     168              : 
     169            5 :     lots = xaccAccountGetLotList(acc);
     170           60 :     for (node = lots; node; node = node->next)
     171              :     {
     172           55 :         GNCLot *lot = GNC_LOT(node->data);
     173           55 :         xaccScrubLot (lot);
     174              :     }
     175            5 :     g_list_free(lots);
     176            5 :     xaccAccountCommitEdit(acc);
     177            5 :     LEAVE ("(acc=%s)", xaccAccountGetName(acc));
     178              : }
     179              : 
     180              : /* ============================================================== */
     181              : 
     182              : static void
     183            4 : lot_scrub_cb (Account *acc, gpointer data)
     184              : {
     185            4 :     if (FALSE == xaccAccountHasTrades (acc)) return;
     186            2 :     xaccAccountScrubLots (acc);
     187              : }
     188              : 
     189              : void
     190            1 : xaccAccountTreeScrubLots (Account *acc)
     191              : {
     192            1 :     if (!acc) return;
     193              : 
     194            1 :     gnc_account_foreach_descendant(acc, lot_scrub_cb, nullptr);
     195            1 :     xaccAccountScrubLots (acc);
     196              : }
     197              : 
     198              : /* ========================== END OF FILE  ========================= */
        

Generated by: LCOV version 2.0-1