GnuCash  4.8a-80-g9825132ea+
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 gnu@gnu.org *
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 "cap-gains.h"
44 #include "gnc-engine.h"
45 #include "gncInvoice.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
96 xaccLotFill (GNCLot *lot)
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 
246  /* generic stop-progress conditions */
247  if (!split) return FALSE;
248  g_return_val_if_fail (split->parent, FALSE);
249 
250  /* If there are no sub-splits, then there's nothing to do. */
251  return xaccSplitHasPeers (split);
252 }
253 
254 /* ================================================================= */
255 
256 
257 /* ================================================================= */
258 
259 /* Remove the guid of b from a. Note that a may not contain the guid
260  * of b, (and v.v.) in which case, it will contain other guids which
261  * establish the links. So merge them back in. */
262 
263 static void
264 remove_guids (Split *sa, Split *sb)
265 {
266  xaccSplitRemovePeerSplit (sa, sb);
267  xaccSplitRemovePeerSplit (sb, sa);
268  xaccSplitMergePeerSplits (sa, sb);
269 }
270 
271 /* The merge_splits() routine causes the amount & value of sb
272  * to be merged into sa; it then destroys sb. It also performs
273  * some other misc cleanup */
274 
275 static void
276 merge_splits (Split *sa, Split *sb)
277 {
278  Account *act;
279  Transaction *txn;
280  gnc_numeric amt, val;
281 
282  act = xaccSplitGetAccount (sb);
283  xaccAccountBeginEdit (act);
284 
285  txn = sa->parent;
286  xaccTransBeginEdit (txn);
287 
288  /* Remove the guid of sb from the 'gemini' of sa */
289  remove_guids (sa, sb);
290 
291  /* Add amount of sb into sa, ditto for value. */
292  amt = xaccSplitGetAmount (sa);
293  amt = gnc_numeric_add_fixed (amt, xaccSplitGetAmount (sb));
294  xaccSplitSetAmount (sa, amt);
295 
296  val = xaccSplitGetValue (sa);
297  val = gnc_numeric_add_fixed (val, xaccSplitGetValue (sb));
298  xaccSplitSetValue (sa, val);
299 
300  /* Set reconcile to no; after this much violence,
301  * no way its reconciled. */
303 
304  /* If sb has associated gains splits, trash them. */
305  if ((sb->gains_split) &&
306  (sb->gains_split->gains & GAINS_STATUS_GAINS))
307  {
308  Transaction *t = sb->gains_split->parent;
309  xaccTransBeginEdit (t);
310  xaccTransDestroy (t);
312  }
313 
314  /* Finally, delete sb */
315  xaccSplitDestroy(sb);
316 
317  xaccTransCommitEdit (txn);
318  xaccAccountCommitEdit (act);
319 }
320 
321 gboolean
322 xaccScrubMergeSubSplits (Split *split, gboolean strict)
323 {
324  gboolean rc = FALSE;
325  Transaction *txn;
326  SplitList *node;
327  GNCLot *lot;
328 
329  if (strict && (FALSE == is_subsplit (split))) return FALSE;
330 
331  txn = split->parent;
332 
333  // Don't mess with splits from an invoice transaction
334  // Those are the responsibility of the business code
335  if (gncInvoiceGetInvoiceFromTxn (txn)) return FALSE;
336 
337  lot = xaccSplitGetLot (split);
338 
339  ENTER ("(Lot=%s)", gnc_lot_get_title(lot));
340 restart:
341  for (node = txn->splits; node; node = node->next)
342  {
343  Split *s = node->data;
344  if (xaccSplitGetLot (s) != lot) continue;
345  if (s == split) continue;
346  if (qof_instance_get_destroying(s)) continue;
347 
348  // Don't mess with splits from an invoice transaction
349  // Those are the responsibility of the business code
350  if (gncInvoiceGetInvoiceFromTxn (s->parent)) return FALSE;
351 
352  if (strict)
353  {
354  /* OK, this split is in the same lot (and thus same account)
355  * as the indicated split. Make sure it is really a subsplit
356  * of the split we started with. It's possible to have two
357  * splits in the same lot and transaction that are not subsplits
358  * of each other, the test-period test suite does this, for
359  * example. Only worry about adjacent sub-splits. By
360  * repeatedly merging adjacent subsplits, we'll get the non-
361  * adjacent ones too. */
362  if (!xaccSplitIsPeerSplit (split, s))
363  continue;
364  }
365 
366  merge_splits (split, s);
367  rc = TRUE;
368  goto restart;
369  }
370  if (rc && gnc_numeric_zero_p (split->amount))
371  {
372  time64 pdate = xaccTransGetDate (txn);
373  gchar *pdatestr = gnc_ctime (&pdate);
374  PWARN ("Result of merge has zero amt!");
375  PWARN ("Transaction details - posted date %s - description %s", pdatestr, xaccTransGetDescription(txn));
376  g_free (pdatestr);
377  }
378  LEAVE (" splits merged=%d", rc);
379  return rc;
380 }
381 
382 gboolean
383 xaccScrubMergeLotSubSplits (GNCLot *lot, gboolean strict)
384 {
385  gboolean rc = FALSE;
386  SplitList *node;
387 
388  if (!lot) return FALSE;
389 
390  ENTER (" ");
391 restart:
392  for (node = gnc_lot_get_split_list(lot); node; node = node->next)
393  {
394  Split *s = node->data;
395  if (!xaccScrubMergeSubSplits(s, strict)) continue;
396 
397  rc = TRUE;
398  goto restart;
399  }
400  LEAVE (" splits merged=%d", rc);
401  return rc;
402 }
403 
404 /* =========================== END OF FILE ======================= */
void xaccSplitSetValue(Split *split, gnc_numeric val)
The xaccSplitSetValue() method sets the value of this split in the transaction&#39;s commodity.
Definition: gmock-Split.cpp:92
gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
Equivalence predicate: Returns TRUE (1) if a and b represent the same number.
gboolean xaccScrubMergeLotSubSplits(GNCLot *lot, gboolean strict)
The xaccScrubMergeLotSubSplits() routine does the same as the xaccScrubMergSubSplits, except that it does it for all of the splits in the lot.
Definition: Scrub2.c:383
gchar * gnc_num_dbg_to_string(gnc_numeric n)
Convert to string.
time64 xaccTransGetDate(const Transaction *trans)
Retrieve the posted date of the transaction.
void xaccLotFill(GNCLot *lot)
The xaccLotFill() routine attempts to assign splits to the indicated lot until the lot balance goes t...
Definition: Scrub2.c:96
SplitList * xaccAccountGetSplitList(const Account *acc)
The xaccAccountGetSplitList() routine returns a pointer to a GList of the splits in the account...
Definition: Account.cpp:3964
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
gboolean xaccSplitDestroy(Split *split)
Destructor.
Definition: Split.c:1470
Utilities to Convert Stock Accounts to use Lots.
gboolean qof_instance_get_destroying(gconstpointer ptr)
Retrieve the flag that indicates whether or not this object is about to be destroyed.
void xaccSplitComputeCapGains(Split *split, Account *gain_acc)
The xaccSplitComputeCapGains() routine computes the cap gains or losses for the indicated split...
Definition: cap-gains.c:528
gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Return a+b.
void xaccAccountAssignLots(Account *acc)
Loop over all splits, and make sure that every split belongs to some lot.
Definition: Scrub2.c:58
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
Use any denominator which gives an exactly correct ratio of numerator to denominator.
Definition: gnc-numeric.h:189
void xaccSplitSetReconcile(Split *split, char recn)
Set the reconcile flag.
void xaccSplitRemovePeerSplit(Split *split, const Split *other_split)
Remove a peer split from this split&#39;s lot-split list.
Definition: Split.c:2057
#define PERR(format, args...)
Log a serious error.
Definition: qoflog.h:244
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
GncInvoice * gncInvoiceGetInvoiceFromTxn(const Transaction *txn)
Given a transaction, find and return the Invoice.
Definition: gncInvoice.c:1339
const char * gnc_lot_get_title(const GNCLot *lot)
Get and set the account title, or the account notes, or the marker.
Definition: gnc-lot.c:456
void xaccTransDestroy(Transaction *trans)
Destroys a transaction.
#define PWARN(format, args...)
Log a warning.
Definition: qoflog.h:250
gboolean xaccSplitHasPeers(const Split *split)
Does this split have peers?
Definition: Split.c:2038
void xaccSplitMergePeerSplits(Split *split, const Split *other_split)
Merge the other_split&#39;s peer splits into split&#39;s peers.
Definition: Split.c:2074
GList SplitList
GList of Split.
Definition: gnc-engine.h:211
void xaccSplitSetAmount(Split *split, gnc_numeric amt)
The xaccSplitSetAmount() method sets the amount in the account&#39;s commodity that the split should have...
Definition: gmock-Split.cpp:77
Account handling public routines.
Implement Accounting Policy Private header File.
SplitList * gnc_lot_get_split_list(const GNCLot *lot)
The gnc_lot_get_split_list() routine returns a GList of all the splits in this lot.
Definition: gnc-lot.c:436
void xaccLotScrubDoubleBalance(GNCLot *lot)
The xaccLotScrubDoubleBalance() routine examines the indicated lot.
Definition: Scrub2.c:164
gboolean xaccSplitIsPeerSplit(const Split *split, const Split *other_split)
Report if a split is a peer of this one.
Definition: Split.c:2044
const char * xaccTransGetDescription(const Transaction *trans)
Gets the transaction Description.
const char * gnc_commodity_get_fullname(const gnc_commodity *cm)
Retrieve the full name for the specified commodity.
gboolean xaccScrubMergeSubSplits(Split *split, gboolean strict)
The xaccScrubMergeSubSplits() routine will merge together all of the splits that were at one time spl...
Definition: Scrub2.c:322
void xaccTransCommitEdit(Transaction *trans)
The xaccTransCommitEdit() method indicates that the changes to the transaction and its splits are com...
void xaccTransBeginEdit(Transaction *trans)
The xaccTransBeginEdit() method must be called before any changes are made to a transaction or any of...
All type declarations for the whole Gnucash engine.
Business Invoice Interface.
Split * xaccSplitAssignToLot(Split *split, GNCLot *lot)
The xaccSplitAssignToLot() routine will fit the indicated split into the indicated lot...
Definition: cap-gains.c:221
gboolean xaccTransGetVoidStatus(const Transaction *trans)
Retrieve information on whether or not a transaction has been voided.
Definition: Transaction.c:2799
gboolean xaccSplitAssign(Split *split)
The`xaccSplitAssign() routine will take the indicated split and, if it doesn&#39;t already belong to a lo...
Definition: cap-gains.c:428
gboolean gnc_lot_is_closed(GNCLot *lot)
The gnc_lot_is_closed() routine returns a boolean flag: is this lot closed? A lot is closed if its ba...
Definition: gnc-lot.c:382
gnc_numeric xaccSplitGetValue(const Split *split)
Returns the value of this split in the transaction&#39;s commodity.
Definition: gmock-Split.cpp:84
void xaccAccountBeginEdit(Account *acc)
The xaccAccountBeginEdit() subroutine is the first phase of a two-phase-commit wrapper for account up...
Definition: Account.cpp:1430
Account * xaccSplitGetAccount(const Split *split)
Returns the account of this split, which was set through xaccAccountInsertSplit().
Definition: gmock-Split.cpp:53
This is the private header for the account structure.
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
gint64 time64
Many systems, including Microsoft Windows and BSD-derived Unixes like Darwin, are retaining the int-3...
Definition: gnc-date.h:93
Account * gnc_lot_get_account(const GNCLot *lot)
The gnc_lot_get_account() routine returns the account with which this lot is associated.
Definition: gnc-lot.c:392
char * gnc_ctime(const time64 *secs)
Return a string representation of a date from a 64-bit time value.
Definition: gnc-date.cpp:267
const char * xaccAccountGetName(const Account *acc)
Get the account&#39;s name.
Definition: Account.cpp:3258
#define GNC_DENOM_AUTO
Values that can be passed as the &#39;denom&#39; argument.
Definition: gnc-numeric.h:246
API for Transactions and Splits (journal entries)
void xaccAccountCommitEdit(Account *acc)
ThexaccAccountCommitEdit() subroutine is the second phase of a two-phase-commit wrapper for account u...
Definition: Account.cpp:1471
Utilities to Automatically Compute Capital Gains/Losses.
GNCPolicy * gnc_account_get_policy(Account *acc)
Get the account&#39;s lot order policy.
Definition: Account.cpp:2084
gboolean gnc_commodity_equiv(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equivalent.
gnc_numeric gnc_lot_get_balance(GNCLot *lot)
The gnc_lot_get_balance() routine returns the balance of the lot.
Definition: gnc-lot.c:530
GNCLot * xaccSplitGetLot(const Split *split)
Returns the pointer to the debited/credited Lot where this split belongs to, or NULL if it doesn&#39;t be...
Definition: Split.c:1881
#define NREC
not reconciled or cleared
Definition: Split.h:74
gnc_numeric xaccSplitGetAmount(const Split *split)
Returns the amount of the split in the account&#39;s commodity.
Definition: gmock-Split.cpp:69