Branch data 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 : 53 : gains_possible (GNCLot *lot)
60 : : {
61 : : SplitList *node;
62 : : Account *acc;
63 : : Split *split;
64 : : gboolean comeq;
65 : : gnc_commodity *acc_commodity;
66 : :
67 : 53 : acc = gnc_lot_get_account (lot);
68 : :
69 : 53 : node = gnc_lot_get_split_list (lot);
70 : 53 : if (!node) return FALSE;
71 : 53 : split = GNC_SPLIT(node->data);
72 : :
73 : 53 : acc_commodity = xaccAccountGetCommodity(acc);
74 : 53 : comeq = gnc_commodity_equiv (acc_commodity, split->parent->common_currency);
75 : 53 : 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 : 53 : xaccScrubLot (GNCLot *lot)
86 : : {
87 : 53 : 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 : 53 : if (!lot) return FALSE;
94 : 53 : ENTER ("(lot=%p) %s", lot, gnc_lot_get_title(lot));
95 : :
96 : 53 : acc = gnc_lot_get_account (lot);
97 : 53 : pcy = gnc_account_get_policy(acc);
98 : 53 : xaccAccountBeginEdit(acc);
99 : 53 : xaccScrubMergeLotSubSplits (lot, TRUE);
100 : :
101 : : /* If the lot balance is zero, we don't need to rebalance */
102 : 53 : lot_baln = gnc_lot_get_balance (lot);
103 : 53 : PINFO ("lot baln=%s for %s", gnc_num_dbg_to_string (lot_baln),
104 : : gnc_lot_get_title(lot));
105 : 53 : 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 : 53 : if (gains_possible (lot))
146 : : {
147 : 52 : xaccLotComputeCapGains (lot, nullptr);
148 : 52 : xaccLotScrubDoubleBalance (lot);
149 : : }
150 : 53 : xaccAccountCommitEdit(acc);
151 : :
152 : 53 : LEAVE ("(lot=%s, deleted=%d)", gnc_lot_get_title(lot), splits_deleted);
153 : 53 : return splits_deleted;
154 : : }
155 : :
156 : : /* ============================================================== */
157 : :
158 : : void
159 : 4 : xaccAccountScrubLots (Account *acc)
160 : : {
161 : : LotList *lots, *node;
162 : 4 : if (!acc) return;
163 : 4 : if (FALSE == xaccAccountHasTrades (acc)) return;
164 : :
165 : 3 : ENTER ("(acc=%s)", xaccAccountGetName(acc));
166 : 3 : xaccAccountBeginEdit(acc);
167 : 3 : xaccAccountAssignLots (acc);
168 : :
169 : 3 : lots = xaccAccountGetLotList(acc);
170 : 56 : for (node = lots; node; node = node->next)
171 : : {
172 : 53 : GNCLot *lot = GNC_LOT(node->data);
173 : 53 : xaccScrubLot (lot);
174 : : }
175 : 3 : g_list_free(lots);
176 : 3 : xaccAccountCommitEdit(acc);
177 : 3 : 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 ========================= */
|