GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
Scrub2.c
Go to the documentation of this file.
1 /********************************************************************\
2  * Scrub2.c -- Convert Stock Accounts to use Lots *
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 [email protected] *
20 \********************************************************************/
21 
33 #include "config.h"
34 
35 #include <glib.h>
36 
37 #include "qof.h"
38 #include "Account.h"
39 #include "AccountP.h"
40 #include "Transaction.h"
41 #include "TransactionP.h"
42 #include "Scrub2.h"
43 #include "ScrubP.h"
44 #include "cap-gains.h"
45 #include "gnc-engine.h"
46 #include "gnc-lot.h"
47 #include "policy-p.h"
48 
49 static QofLogModule log_module = GNC_MOD_LOT;
50 
51 /* ============================================================== */
57 void
59 {
60  SplitList *splits, *node;
61 
62  if (!acc) return;
63 
64  ENTER ("acc=%s", xaccAccountGetName(acc));
66 
67 restart_loop:
68  splits = xaccAccountGetSplitList(acc);
69  for (node = splits; node; node = node->next)
70  {
71  Split * split = node->data;
72 
73  /* If already in lot, then no-op */
74  if (split->lot) continue;
75 
76  /* Skip voided transactions */
77  if (gnc_numeric_zero_p (split->amount) &&
78  xaccTransGetVoidStatus(split->parent)) continue;
79 
80  if (xaccSplitAssign (split)) goto restart_loop;
81  }
83  LEAVE ("acc=%s", xaccAccountGetName(acc));
84 }
85 
86 /* ============================================================== */
87 
95 void
97 {
98  Account *acc;
99  Split *split;
100  GNCPolicy *pcy;
101 
102  if (!lot) return;
103  acc = gnc_lot_get_account(lot);
104  pcy = gnc_account_get_policy(acc);
105 
106  ENTER ("(lot=%s, acc=%s)", gnc_lot_get_title(lot), xaccAccountGetName(acc));
107 
108  /* If balance already zero, we have nothing to do. */
109  if (gnc_lot_is_closed (lot))
110  {
111  LEAVE ("Lot Closed (lot=%s, acc=%s)", gnc_lot_get_title(lot),
112  xaccAccountGetName(acc));
113  return;
114  }
115  split = pcy->PolicyGetSplit (pcy, lot);
116  if (!split)
117  {
118  LEAVE ("No Split (lot=%s, acc=%s)", gnc_lot_get_title(lot),
119  xaccAccountGetName(acc));
120  return; /* Handle the common case */
121  }
122 
123  /* Reject voided transactions */
124  if (gnc_numeric_zero_p(split->amount) &&
125  xaccTransGetVoidStatus(split->parent))
126  {
127  LEAVE ("Voided transaction (lot=%s, acc=%s)",
129  return;
130  }
131 
132  xaccAccountBeginEdit (acc);
133 
134  /* Loop until we've filled up the lot, (i.e. till the
135  * balance goes to zero) or there are no splits left. */
136  while (1)
137  {
138  Split *subsplit;
139 
140  subsplit = xaccSplitAssignToLot (split, lot);
141  if (subsplit == split)
142  {
143  PERR ("Accounting Policy gave us a split that "
144  "doesn't fit into this lot\n"
145  "lot baln=%s, isclosed=%d, aplit amt=%s",
147  gnc_lot_is_closed (lot),
148  gnc_num_dbg_to_string (split->amount));
149  break;
150  }
151 
152  if (gnc_lot_is_closed (lot)) break;
153 
154  split = pcy->PolicyGetSplit (pcy, lot);
155  if (!split) break;
156  }
157  xaccAccountCommitEdit (acc);
158  LEAVE ("(lot=%s, acc=%s)", gnc_lot_get_title(lot), xaccAccountGetName(acc));
159 }
160 
161 /* ============================================================== */
162 
163 void
165 {
166  gnc_commodity *currency = NULL;
167  SplitList *snode;
168  GList *node;
169  gnc_numeric zero = gnc_numeric_zero();
170  gnc_numeric value = zero;
171 
172  if (!lot) return;
173 
174  ENTER ("lot=%s", gnc_lot_get_title(lot));
175 
176  for (snode = gnc_lot_get_split_list(lot); snode; snode = snode->next)
177  {
178  Split *s = snode->data;
179  xaccSplitComputeCapGains (s, NULL);
180  }
181 
182  /* We double-check only closed lots */
183  if (FALSE == gnc_lot_is_closed (lot))
184  {
185  LEAVE ("lot=%s is closed", gnc_lot_get_title(lot));
186  return;
187  }
188 
189  for (snode = gnc_lot_get_split_list(lot); snode; snode = snode->next)
190  {
191  Split *s = snode->data;
192  Transaction *trans = s->parent;
193 
194  /* Check to make sure all splits in the lot have a common currency */
195  if (NULL == currency)
196  {
197  currency = trans->common_currency;
198  }
199  if (FALSE == gnc_commodity_equiv (currency, trans->common_currency))
200  {
201  /* This lot has mixed currencies. Can't double-balance.
202  * Silently punt */
203  PWARN ("Lot with multiple currencies:\n"
204  "\ttrans=%s curr=%s", xaccTransGetDescription(trans),
205  gnc_commodity_get_fullname(trans->common_currency));
206  break;
207  }
208 
209  /* Now, total up the values */
210  value = gnc_numeric_add (value, xaccSplitGetValue (s),
212  PINFO ("Split=%p value=%s Accum Lot value=%s", s,
213  gnc_num_dbg_to_string (s->value),
214  gnc_num_dbg_to_string (value));
215 
216  }
217 
218  if (FALSE == gnc_numeric_equal (value, zero))
219  {
220  /* Unhandled error condition. Not sure what to do here,
221  * Since the ComputeCapGains should have gotten it right.
222  * I suppose there might be small rounding errors, a penny or two,
223  * the ideal thing would to figure out why there's a rounding
224  * error, and fix that.
225  */
226  PERR ("Closed lot fails to double-balance !! lot value=%s",
227  gnc_num_dbg_to_string (value));
228  for (node = gnc_lot_get_split_list(lot); node; node = node->next)
229  {
230  Split *s = node->data;
231  PERR ("s=%p amt=%s val=%s", s,
232  gnc_num_dbg_to_string(s->amount),
233  gnc_num_dbg_to_string(s->value));
234  }
235  }
236 
237  LEAVE ("lot=%s", gnc_lot_get_title(lot));
238 }
239 
240 /* ================================================================= */
241 
242 static inline gboolean
243 is_subsplit (Split *split)
244 {
245  KvpValue *kval;
246 
247  /* generic stop-progress conditions */
248  if (!split) return FALSE;
249  g_return_val_if_fail (split->parent, FALSE);
250 
251  /* If there are no sub-splits, then there's nothing to do. */
252  kval = kvp_frame_get_slot (split->inst.kvp_data, "lot-split");
253  if (!kval) return FALSE;
254 
255  return TRUE;
256 }
257 
258 /* ================================================================= */
259 
260 
261 /* ================================================================= */
262 
263 /* Remove the guid of b from a. Note that a may not contain the guid
264  * of b, (and v.v.) in which case, it will contain other guids which
265  * establish the links. So merge them back in. */
266 
267 static void
268 remove_guids (Split *sa, Split *sb)
269 {
270  KvpFrame *ksub;
271 
272  /* Find and remove the matching guid's */
273  ksub = (KvpFrame*)gnc_kvp_bag_find_by_guid (sa->inst.kvp_data, "lot-split",
274  "peer_guid", qof_instance_get_guid(sb));
275  if (ksub)
276  {
277  gnc_kvp_bag_remove_frame (sa->inst.kvp_data, "lot-split", ksub);
278  kvp_frame_delete (ksub);
279  }
280 
281  /* Now do it in the other direction */
282  ksub = (KvpFrame*)gnc_kvp_bag_find_by_guid (sb->inst.kvp_data, "lot-split",
283  "peer_guid", qof_instance_get_guid(sa));
284  if (ksub)
285  {
286  gnc_kvp_bag_remove_frame (sb->inst.kvp_data, "lot-split", ksub);
287  kvp_frame_delete (ksub);
288  }
289 
290  /* Finally, merge b's lot-splits, if any, into a's */
291  /* This is an important step, if it got busted into many pieces. */
292  gnc_kvp_bag_merge (sa->inst.kvp_data, "lot-split",
293  sb->inst.kvp_data, "lot-split");
294 }
295 
296 /* The merge_splits() routine causes the amount & value of sb
297  * to be merged into sa; it then destroys sb. It also performs
298  * some other misc cleanup */
299 
300 static void
301 merge_splits (Split *sa, Split *sb)
302 {
303  Account *act;
304  Transaction *txn;
305  gnc_numeric amt, val;
306 
307  act = xaccSplitGetAccount (sb);
308  xaccAccountBeginEdit (act);
309 
310  txn = sa->parent;
311  xaccTransBeginEdit (txn);
312 
313  /* Remove the guid of sb from the 'gemini' of sa */
314  remove_guids (sa, sb);
315 
316  /* Add amount of sb into sa, ditto for value. */
317  amt = xaccSplitGetAmount (sa);
318  amt = gnc_numeric_add_fixed (amt, xaccSplitGetAmount (sb));
319  xaccSplitSetAmount (sa, amt);
320 
321  val = xaccSplitGetValue (sa);
322  val = gnc_numeric_add_fixed (val, xaccSplitGetValue (sb));
323  xaccSplitSetValue (sa, val);
324 
325  /* Set reconcile to no; after this much violence,
326  * no way its reconciled. */
328 
329  /* If sb has associated gains splits, trash them. */
330  if ((sb->gains_split) &&
331  (sb->gains_split->gains & GAINS_STATUS_GAINS))
332  {
333  Transaction *t = sb->gains_split->parent;
334  xaccTransBeginEdit (t);
335  xaccTransDestroy (t);
337  }
338 
339  /* Finally, delete sb */
340  xaccSplitDestroy(sb);
341 
342  xaccTransCommitEdit (txn);
343  xaccAccountCommitEdit (act);
344 }
345 
346 gboolean
347 xaccScrubMergeSubSplits (Split *split, gboolean strict)
348 {
349  gboolean rc = FALSE;
350  Transaction *txn;
351  SplitList *node;
352  GNCLot *lot;
353  const GncGUID *guid;
354 
355  if (strict && (FALSE == is_subsplit (split))) return FALSE;
356 
357  txn = split->parent;
358  lot = xaccSplitGetLot (split);
359 
360  ENTER ("(Lot=%s)", gnc_lot_get_title(lot));
361 restart:
362  for (node = txn->splits; node; node = node->next)
363  {
364  Split *s = node->data;
365  if (xaccSplitGetLot (s) != lot) continue;
366  if (s == split) continue;
367  if (qof_instance_get_destroying(s)) continue;
368 
369  if (strict)
370  {
371  /* OK, this split is in the same lot (and thus same account)
372  * as the indicated split. Make sure it is really a subsplit
373  * of the split we started with. It's possible to have two
374  * splits in the same lot and transaction that are not subsplits
375  * of each other, the test-period test suite does this, for
376  * example. Only worry about adjacent sub-splits. By
377  * repeatedly merging adjacent subsplits, we'll get the non-
378  * adjacent ones too. */
379  guid = qof_instance_get_guid(s);
380  if (gnc_kvp_bag_find_by_guid (split->inst.kvp_data, "lot-split",
381  "peer_guid", guid) == NULL)
382  continue;
383  }
384 
385  merge_splits (split, s);
386  rc = TRUE;
387  goto restart;
388  }
389  if (gnc_numeric_zero_p (split->amount))
390  {
391  PWARN ("Result of merge has zero amt!");
392  }
393  LEAVE (" splits merged=%d", rc);
394  return rc;
395 }
396 
397 gboolean
398 xaccScrubMergeLotSubSplits (GNCLot *lot, gboolean strict)
399 {
400  gboolean rc = FALSE;
401  SplitList *node;
402 
403  if (!lot) return FALSE;
404 
405  ENTER (" ");
406 restart:
407  for (node = gnc_lot_get_split_list(lot); node; node = node->next)
408  {
409  Split *s = node->data;
410  if (!xaccScrubMergeSubSplits(s, strict)) continue;
411 
412  rc = TRUE;
413  goto restart;
414  }
415  LEAVE (" splits merged=%d", rc);
416  return rc;
417 }
418 
419 /* =========================== END OF FILE ======================= */
void xaccSplitSetValue(Split *s, gnc_numeric amt)
Definition: Split.c:1294
gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
gboolean xaccScrubMergeLotSubSplits(GNCLot *lot, gboolean strict)
Definition: Scrub2.c:398
gchar * gnc_num_dbg_to_string(gnc_numeric n)
const GncGUID * qof_instance_get_guid(gconstpointer)
void xaccLotFill(GNCLot *lot)
Definition: Scrub2.c:96
SplitList * xaccAccountGetSplitList(const Account *acc)
Definition: Account.c:3717
#define PINFO(format, args...)
Definition: qoflog.h:249
gboolean xaccSplitDestroy(Split *split)
Definition: Split.c:1492
Utilities to Convert Stock Accounts to use Lots.
gboolean qof_instance_get_destroying(gconstpointer ptr)
void xaccSplitComputeCapGains(Split *split, Account *gain_acc)
Definition: cap-gains.c:536
gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
void xaccAccountAssignLots(Account *acc)
Definition: Scrub2.c:58
gboolean gnc_numeric_zero_p(gnc_numeric a)
void xaccSplitSetReconcile(Split *split, char recn)
Definition: Split.c:1826
#define PERR(format, args...)
Definition: qoflog.h:237
#define ENTER(format, args...)
Definition: qoflog.h:261
void kvp_frame_delete(KvpFrame *frame)
Definition: guid.h:65
void gnc_kvp_bag_merge(KvpFrame *kvp_into, const char *intopath, KvpFrame *kvp_from, const char *frompath)
const char * gnc_lot_get_title(const GNCLot *lot)
Definition: gnc-lot.c:437
void xaccTransDestroy(Transaction *trans)
Definition: Transaction.c:1402
#define PWARN(format, args...)
Definition: qoflog.h:243
GList SplitList
Definition: gnc-engine.h:203
void xaccSplitSetAmount(Split *s, gnc_numeric amt)
Definition: Split.c:1258
Account handling public routines.
Implement Accounting Policy Private header File.
SplitList * gnc_lot_get_split_list(const GNCLot *lot)
Definition: gnc-lot.c:417
void xaccLotScrubDoubleBalance(GNCLot *lot)
Definition: Scrub2.c:164
const char * xaccTransGetDescription(const Transaction *trans)
Definition: Transaction.c:2184
const char * gnc_commodity_get_fullname(const gnc_commodity *cm)
gboolean xaccScrubMergeSubSplits(Split *split, gboolean strict)
Definition: Scrub2.c:347
void xaccTransCommitEdit(Transaction *trans)
Definition: Transaction.c:1579
void xaccTransBeginEdit(Transaction *trans)
Definition: Transaction.c:1380
All type declarations for the whole Gnucash engine.
KvpFrame * gnc_kvp_bag_find_by_guid(KvpFrame *root, const char *path, const char *guid_name, const GncGUID *desired_guid)
Split * xaccSplitAssignToLot(Split *split, GNCLot *lot)
Definition: cap-gains.c:222
gboolean xaccTransGetVoidStatus(const Transaction *trans)
Definition: Transaction.c:2526
gboolean xaccSplitAssign(Split *split)
Definition: cap-gains.c:438
gboolean gnc_lot_is_closed(GNCLot *lot)
Definition: gnc-lot.c:376
Definition: SplitP.h:71
void gnc_kvp_bag_remove_frame(KvpFrame *root, const char *path, KvpFrame *fr)
gnc_numeric xaccSplitGetValue(const Split *split)
Definition: Split.c:1993
void xaccAccountBeginEdit(Account *acc)
Definition: Account.c:1280
Account * xaccSplitGetAccount(const Split *s)
Definition: Split.c:968
struct KvpFrameImpl KvpFrame
Definition: kvp_frame.h:76
#define LEAVE(format, args...)
Definition: qoflog.h:271
Account * gnc_lot_get_account(const GNCLot *lot)
Definition: gnc-lot.c:386
const char * xaccAccountGetName(const Account *acc)
Definition: Account.c:3031
#define GNC_DENOM_AUTO
Definition: gnc-numeric.h:246
API for Transactions and Splits (journal entries)
void xaccAccountCommitEdit(Account *acc)
Definition: Account.c:1321
Utilities to Automatically Compute Capital Gains/Losses.
struct KvpValueImpl KvpValue
Definition: kvp_frame.h:80
GNCPolicy * gnc_account_get_policy(Account *acc)
Definition: Account.c:1880
gboolean gnc_commodity_equiv(const gnc_commodity *a, const gnc_commodity *b)
gnc_numeric gnc_lot_get_balance(GNCLot *lot)
Definition: gnc-lot.c:477
GNCLot * xaccSplitGetLot(const Split *split)
Definition: Split.c:1953
const gchar * QofLogModule
Definition: qofid.h:89
#define NREC
Definition: Split.h:70
gnc_numeric xaccSplitGetAmount(const Split *split)
Definition: Split.c:1987