mirror of
https://github.com/Gnucash/gnucash.git
synced 2024-11-29 20:24:25 -06:00
1238b9d8cd
This will avoid a ninja-build from picking up a config.h generated by the autotools build (in the root build directory). Picking up the wrong config.h may lead to all kinds of subtle issues if the autotools run was done with different options than the cmake run.
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.h"
|
|
#include "Scrub2.h"
|
|
#include "Scrub3.h"
|
|
#include "Transaction.h"
|
|
#include "TransactionP.h"
|
|
|
|
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 = 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, NULL, NULL);
|
|
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 = 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, NULL);
|
|
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 = 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, NULL);
|
|
xaccAccountScrubLots (acc);
|
|
}
|
|
|
|
/* ========================== END OF FILE ========================= */
|