mirror of
https://github.com/Gnucash/gnucash.git
synced 2024-11-22 08:57:17 -06:00
199 lines
6.6 KiB
C++
199 lines
6.6 KiB
C++
/********************************************************************\
|
|
* Scrub3.c -- Constrain Cap Gains to Track Sources of Gains *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or *
|
|
* modify it under the terms of the GNU General Public License as *
|
|
* published by the Free Software Foundation; either version 2 of *
|
|
* the License, or (at your option) any later version. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License*
|
|
* along with this program; if not, contact: *
|
|
* *
|
|
* Free Software Foundation Voice: +1-617-542-5942 *
|
|
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
|
|
* Boston, MA 02110-1301, USA gnu@gnu.org *
|
|
\********************************************************************/
|
|
|
|
/** @file Scrub3.c
|
|
* @brief Constrain Cap Gains to Track Sources of Gains
|
|
* @author Created by Linas Vepstas Sept 2003
|
|
* @author Copyright (c) 2003,2004 Linas Vepstas <linas@linas.org>
|
|
*
|
|
* Provides a set of functions and utilities for checking and
|
|
* repairing ('scrubbing clean') the usage of Cap Gains
|
|
* transactions in stock and commodity accounts.
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <glib.h>
|
|
|
|
#include "cap-gains.h"
|
|
#include "gnc-commodity.h"
|
|
#include "gnc-engine.h"
|
|
#include "gnc-lot.h"
|
|
#include "policy-p.h"
|
|
#include "Account.h"
|
|
#include "AccountP.hpp"
|
|
#include "Scrub2.h"
|
|
#include "Scrub3.h"
|
|
#include "Transaction.h"
|
|
#include "TransactionP.hpp"
|
|
|
|
static QofLogModule log_module = GNC_MOD_LOT;
|
|
|
|
/* ================================================================= */
|
|
/** Cap gains are possible only if the lot commodity is not the same
|
|
* as the transaction currency. We assume here that all splits in
|
|
* the lot share the same transaction currency, and so we look at
|
|
* the first split, and see what it's currency is.
|
|
* This routine returns TRUE if cap gains are possible.
|
|
*/
|
|
|
|
static inline gboolean
|
|
gains_possible (GNCLot *lot)
|
|
{
|
|
SplitList *node;
|
|
Account *acc;
|
|
Split *split;
|
|
gboolean comeq;
|
|
gnc_commodity *acc_commodity;
|
|
|
|
acc = gnc_lot_get_account (lot);
|
|
|
|
node = gnc_lot_get_split_list (lot);
|
|
if (!node) return FALSE;
|
|
split = GNC_SPLIT(node->data);
|
|
|
|
acc_commodity = xaccAccountGetCommodity(acc);
|
|
comeq = gnc_commodity_equiv (acc_commodity, split->parent->common_currency);
|
|
return (FALSE == comeq);
|
|
}
|
|
|
|
/* ================================================================= */
|
|
/* XXX What happens if, as a result of scrubbing, the lot is empty?
|
|
* I don't think this is handled properly. I think that what will
|
|
* happen is we'll end up with an empty, closed lot ... ?
|
|
*/
|
|
|
|
gboolean
|
|
xaccScrubLot (GNCLot *lot)
|
|
{
|
|
gboolean splits_deleted = FALSE;
|
|
gnc_numeric lot_baln;
|
|
gboolean opening_baln_is_pos, lot_baln_is_pos;
|
|
Account *acc;
|
|
GNCPolicy *pcy;
|
|
|
|
if (!lot) return FALSE;
|
|
ENTER ("(lot=%p) %s", lot, gnc_lot_get_title(lot));
|
|
|
|
acc = gnc_lot_get_account (lot);
|
|
pcy = gnc_account_get_policy(acc);
|
|
xaccAccountBeginEdit(acc);
|
|
xaccScrubMergeLotSubSplits (lot, TRUE);
|
|
|
|
/* If the lot balance is zero, we don't need to rebalance */
|
|
lot_baln = gnc_lot_get_balance (lot);
|
|
PINFO ("lot baln=%s for %s", gnc_num_dbg_to_string (lot_baln),
|
|
gnc_lot_get_title(lot));
|
|
if (! gnc_numeric_zero_p (lot_baln))
|
|
{
|
|
SplitList *node;
|
|
gnc_numeric opening_baln;
|
|
|
|
/* Get the opening balance for this lot */
|
|
pcy->PolicyGetLotOpening (pcy, lot, &opening_baln, nullptr, nullptr);
|
|
PINFO ("lot opener baln=%s", gnc_num_dbg_to_string (opening_baln));
|
|
|
|
/* If the lot is fat, give the boot to all the non-opening
|
|
* splits, and refill it */
|
|
opening_baln_is_pos = gnc_numeric_positive_p(opening_baln);
|
|
lot_baln_is_pos = gnc_numeric_positive_p(lot_baln);
|
|
if ((opening_baln_is_pos || lot_baln_is_pos) &&
|
|
((!opening_baln_is_pos) || (!lot_baln_is_pos)))
|
|
{
|
|
rethin:
|
|
for (node = gnc_lot_get_split_list(lot); node; node = node->next)
|
|
{
|
|
Split *s = GNC_SPLIT(node->data);
|
|
if (pcy->PolicyIsOpeningSplit (pcy, lot, s)) continue;
|
|
gnc_lot_remove_split (lot, s);
|
|
goto rethin;
|
|
}
|
|
}
|
|
|
|
/* At this point the lot is thin, so try to fill it */
|
|
xaccLotFill (lot);
|
|
|
|
/* Make sure there are no subsplits. */
|
|
splits_deleted = xaccScrubMergeLotSubSplits (lot, TRUE);
|
|
}
|
|
|
|
/* Now re-compute cap gains, and then double-check that.
|
|
* But we only compute cap-gains if gains are possible;
|
|
* that is if the lot commodity is not the same as the
|
|
* currency. That is, one can't possibly have gains
|
|
* selling dollars for dollars. The business modules
|
|
* use lots with lot commodity == lot currency.
|
|
*/
|
|
if (gains_possible (lot))
|
|
{
|
|
xaccLotComputeCapGains (lot, nullptr);
|
|
xaccLotScrubDoubleBalance (lot);
|
|
}
|
|
xaccAccountCommitEdit(acc);
|
|
|
|
LEAVE ("(lot=%s, deleted=%d)", gnc_lot_get_title(lot), splits_deleted);
|
|
return splits_deleted;
|
|
}
|
|
|
|
/* ============================================================== */
|
|
|
|
void
|
|
xaccAccountScrubLots (Account *acc)
|
|
{
|
|
LotList *lots, *node;
|
|
if (!acc) return;
|
|
if (FALSE == xaccAccountHasTrades (acc)) return;
|
|
|
|
ENTER ("(acc=%s)", xaccAccountGetName(acc));
|
|
xaccAccountBeginEdit(acc);
|
|
xaccAccountAssignLots (acc);
|
|
|
|
lots = xaccAccountGetLotList(acc);
|
|
for (node = lots; node; node = node->next)
|
|
{
|
|
GNCLot *lot = GNC_LOT(node->data);
|
|
xaccScrubLot (lot);
|
|
}
|
|
g_list_free(lots);
|
|
xaccAccountCommitEdit(acc);
|
|
LEAVE ("(acc=%s)", xaccAccountGetName(acc));
|
|
}
|
|
|
|
/* ============================================================== */
|
|
|
|
static void
|
|
lot_scrub_cb (Account *acc, gpointer data)
|
|
{
|
|
if (FALSE == xaccAccountHasTrades (acc)) return;
|
|
xaccAccountScrubLots (acc);
|
|
}
|
|
|
|
void
|
|
xaccAccountTreeScrubLots (Account *acc)
|
|
{
|
|
if (!acc) return;
|
|
|
|
gnc_account_foreach_descendant(acc, lot_scrub_cb, nullptr);
|
|
xaccAccountScrubLots (acc);
|
|
}
|
|
|
|
/* ========================== END OF FILE ========================= */
|