/********************************************************************\ * 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 * * 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 #include #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 ========================= */