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