GnuCash 2.4.99
Scrub3.c
Go to the documentation of this file.
00001 /********************************************************************\
00002  * Scrub3.c -- Constrain Cap Gains to Track Sources of Gains        *
00003  *                                                                  *
00004  * This program is free software; you can redistribute it and/or    *
00005  * modify it under the terms of the GNU General Public License as   *
00006  * published by the Free Software Foundation; either version 2 of   *
00007  * the License, or (at your option) any later version.              *
00008  *                                                                  *
00009  * This program is distributed in the hope that it will be useful,  *
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
00012  * GNU General Public License for more details.                     *
00013  *                                                                  *
00014  * You should have received a copy of the GNU General Public License*
00015  * along with this program; if not, contact:                        *
00016  *                                                                  *
00017  * Free Software Foundation           Voice:  +1-617-542-5942       *
00018  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
00019  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
00020 \********************************************************************/
00021 
00032 #include "config.h"
00033 
00034 #include <glib.h>
00035 
00036 #include "cap-gains.h"
00037 #include "gnc-commodity.h"
00038 #include "gnc-engine.h"
00039 #include "gnc-lot.h"
00040 #include "policy-p.h"
00041 #include "Account.h"
00042 #include "AccountP.h"
00043 #include "Scrub2.h"
00044 #include "Scrub3.h"
00045 #include "Transaction.h"
00046 #include "TransactionP.h"
00047 
00048 static QofLogModule log_module = GNC_MOD_LOT;
00049 
00050 /* ================================================================= */
00058 static inline gboolean
00059 gains_possible (GNCLot *lot)
00060 {
00061     SplitList *node;
00062     Account *acc;
00063     Split *split;
00064     gboolean comeq;
00065     gnc_commodity *acc_commodity;
00066 
00067     acc = gnc_lot_get_account (lot);
00068 
00069     node = gnc_lot_get_split_list (lot);
00070     if (!node) return FALSE;
00071     split = node->data;
00072 
00073     acc_commodity = xaccAccountGetCommodity(acc);
00074     comeq = gnc_commodity_equiv (acc_commodity, split->parent->common_currency);
00075     return (FALSE == comeq);
00076 }
00077 
00078 /* ================================================================= */
00079 /* XXX What happens if, as a result of scrubbing, the lot is empty?
00080  * I don't think this is handled properly.  I think that what will
00081  * happen is we'll end up with an empty, closed lot ... ?
00082  */
00083 
00084 gboolean
00085 xaccScrubLot (GNCLot *lot)
00086 {
00087     gboolean splits_deleted = FALSE;
00088     gnc_numeric lot_baln;
00089     gboolean opening_baln_is_pos, lot_baln_is_pos;
00090     Account *acc;
00091     GNCPolicy *pcy;
00092 
00093     if (!lot) return FALSE;
00094     ENTER ("(lot=%p) %s", lot, gnc_lot_get_title(lot));
00095 
00096     acc = gnc_lot_get_account (lot);
00097     pcy = gnc_account_get_policy(acc);
00098     xaccAccountBeginEdit(acc);
00099     xaccScrubMergeLotSubSplits (lot);
00100 
00101     /* If the lot balance is zero, we don't need to rebalance */
00102     lot_baln = gnc_lot_get_balance (lot);
00103     PINFO ("lot baln=%s for %s", gnc_num_dbg_to_string (lot_baln),
00104            gnc_lot_get_title(lot));
00105     if (! gnc_numeric_zero_p (lot_baln))
00106     {
00107         SplitList *node;
00108         gnc_numeric opening_baln;
00109 
00110         /* Get the opening balance for this lot */
00111         pcy->PolicyGetLotOpening (pcy, lot, &opening_baln, NULL, NULL);
00112         PINFO ("lot opener baln=%s", gnc_num_dbg_to_string (opening_baln));
00113 
00114         /* If the lot is fat, give the boot to all the non-opening
00115          * splits, and refill it */
00116         opening_baln_is_pos = gnc_numeric_positive_p(opening_baln);
00117         lot_baln_is_pos = gnc_numeric_positive_p(lot_baln);
00118         if ((opening_baln_is_pos || lot_baln_is_pos) &&
00119                 ((!opening_baln_is_pos) || (!lot_baln_is_pos)))
00120         {
00121 rethin:
00122             for (node = gnc_lot_get_split_list(lot); node; node = node->next)
00123             {
00124                 Split *s = node->data;
00125                 if (pcy->PolicyIsOpeningSplit (pcy, lot, s)) continue;
00126                 gnc_lot_remove_split (lot, s);
00127                 goto rethin;
00128             }
00129         }
00130 
00131         /* At this point the lot is thin, so try to fill it */
00132         xaccLotFill (lot);
00133 
00134         /* Make sure there are no subsplits. */
00135         splits_deleted = xaccScrubMergeLotSubSplits (lot);
00136     }
00137 
00138     /* Now re-compute cap gains, and then double-check that.
00139      * But we only compute cap-gains if gains are possible;
00140      * that is if the lot commodity is not the same as the
00141      * currency. That is, one can't possibly have gains
00142      * selling dollars for dollars.  The business modules
00143      * use lots with lot commodity == lot currency.
00144      */
00145     if (gains_possible (lot))
00146     {
00147         xaccLotComputeCapGains (lot, NULL);
00148         xaccLotScrubDoubleBalance (lot);
00149     }
00150     xaccAccountCommitEdit(acc);
00151 
00152     LEAVE ("(lot=%s, deleted=%d)", gnc_lot_get_title(lot), splits_deleted);
00153     return splits_deleted;
00154 }
00155 
00156 /* ============================================================== */
00157 
00158 void
00159 xaccAccountScrubLots (Account *acc)
00160 {
00161     LotList *lots, *node;
00162     if (!acc) return;
00163     if (FALSE == xaccAccountHasTrades (acc)) return;
00164 
00165     ENTER ("(acc=%s)", xaccAccountGetName(acc));
00166     xaccAccountBeginEdit(acc);
00167     xaccAccountAssignLots (acc);
00168 
00169     lots = xaccAccountGetLotList(acc);
00170     for (node = lots; node; node = node->next)
00171     {
00172         GNCLot *lot = node->data;
00173         xaccScrubLot (lot);
00174     }
00175     g_list_free(lots);
00176     xaccAccountCommitEdit(acc);
00177     LEAVE ("(acc=%s)", xaccAccountGetName(acc));
00178 }
00179 
00180 /* ============================================================== */
00181 
00182 static void
00183 lot_scrub_cb (Account *acc, gpointer data)
00184 {
00185     if (FALSE == xaccAccountHasTrades (acc)) return;
00186     xaccAccountScrubLots (acc);
00187 }
00188 
00189 void
00190 xaccAccountTreeScrubLots (Account *acc)
00191 {
00192     if (!acc) return;
00193 
00194     gnc_account_foreach_descendant(acc, lot_scrub_cb, NULL);
00195     xaccAccountScrubLots (acc);
00196 }
00197 
00198 /* ========================== END OF FILE  ========================= */
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines