GnuCash  5.6-150-g038405b370+
Scrub2.cpp
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.hpp"
40 #include "Account.hpp"
41 #include "Transaction.h"
42 #include "TransactionP.hpp"
43 #include "Scrub2.h"
44 #include "cap-gains.h"
45 #include "gnc-engine.h"
46 #include "gncInvoice.h"
47 #include "gnc-lot.h"
48 #include "policy-p.h"
49 
50 static QofLogModule log_module = GNC_MOD_LOT;
51 
52 /* ============================================================== */
58 void
60 {
61  if (!acc) return;
62 
63  ENTER ("acc=%s", xaccAccountGetName(acc));
65 
66 restart_loop:
67  for (auto split : xaccAccountGetSplits (acc))
68  {
69  /* If already in lot, then no-op */
70  if (split->lot) continue;
71 
72  /* Skip stock splits */
73  if (xaccSplitIsStockSplit(split)) continue;
74 
75  /* Skip voided transactions */
76  if (gnc_numeric_zero_p (split->amount) &&
77  xaccTransGetVoidStatus(split->parent)) continue;
78 
79  if (xaccSplitAssign (split)) goto restart_loop;
80  }
82  LEAVE ("acc=%s", xaccAccountGetName(acc));
83 }
84 
85 /* ============================================================== */
86 
94 void
95 xaccLotFill (GNCLot *lot)
96 {
97  Account *acc;
98  Split *split;
99  GNCPolicy *pcy;
100 
101  if (!lot) return;
102  acc = gnc_lot_get_account(lot);
103  pcy = gnc_account_get_policy(acc);
104 
105  ENTER ("(lot=%s, acc=%s)", gnc_lot_get_title(lot), xaccAccountGetName(acc));
106 
107  /* If balance already zero, we have nothing to do. */
108  if (gnc_lot_is_closed (lot))
109  {
110  LEAVE ("Lot Closed (lot=%s, acc=%s)", gnc_lot_get_title(lot),
111  xaccAccountGetName(acc));
112  return;
113  }
114  split = pcy->PolicyGetSplit (pcy, lot);
115  if (!split)
116  {
117  LEAVE ("No Split (lot=%s, acc=%s)", gnc_lot_get_title(lot),
118  xaccAccountGetName(acc));
119  return; /* Handle the common case */
120  }
121 
122  /* Reject stock split transactions */
123  if (xaccSplitIsStockSplit(split))
124  {
125  LEAVE ("Stock split transaction (lot=%s, acc=%s)",
126  gnc_lot_get_title(lot), xaccAccountGetName(acc));
127  return;
128  }
129 
130  /* Reject voided transactions */
131  if (gnc_numeric_zero_p(split->amount) &&
132  xaccTransGetVoidStatus(split->parent))
133  {
134  LEAVE ("Voided transaction (lot=%s, acc=%s)",
135  gnc_lot_get_title(lot), xaccAccountGetName(acc));
136  return;
137  }
138 
139  xaccAccountBeginEdit (acc);
140 
141  /* Loop until we've filled up the lot, (i.e. till the
142  * balance goes to zero) or there are no splits left. */
143  while (1)
144  {
145  Split *subsplit;
146 
147  subsplit = xaccSplitAssignToLot (split, lot);
148  if (subsplit == split)
149  {
150  PERR ("Accounting Policy gave us a split that "
151  "doesn't fit into this lot\n"
152  "lot baln=%s, isclosed=%d, aplit amt=%s",
154  gnc_lot_is_closed (lot),
155  gnc_num_dbg_to_string (split->amount));
156  break;
157  }
158 
159  if (gnc_lot_is_closed (lot)) break;
160 
161  split = pcy->PolicyGetSplit (pcy, lot);
162  if (!split) break;
163  }
164  xaccAccountCommitEdit (acc);
165  LEAVE ("(lot=%s, acc=%s)", gnc_lot_get_title(lot), xaccAccountGetName(acc));
166 }
167 
168 /* ============================================================== */
169 
170 void
172 {
173  gnc_commodity *currency = nullptr;
174  SplitList *snode;
175  GList *node;
176  gnc_numeric zero = gnc_numeric_zero();
177  gnc_numeric value = zero;
178 
179  if (!lot) return;
180 
181  ENTER ("lot=%s", gnc_lot_get_title(lot));
182 
183  for (snode = gnc_lot_get_split_list(lot); snode; snode = snode->next)
184  {
185  Split *s = GNC_SPLIT(snode->data);
186  xaccSplitComputeCapGains (s, nullptr);
187  }
188 
189  /* We double-check only closed lots */
190  if (FALSE == gnc_lot_is_closed (lot))
191  {
192  LEAVE ("lot=%s is closed", gnc_lot_get_title(lot));
193  return;
194  }
195 
196  for (snode = gnc_lot_get_split_list(lot); snode; snode = snode->next)
197  {
198  Split *s = GNC_SPLIT(snode->data);
199  Transaction *trans = s->parent;
200 
201  /* Check to make sure all splits in the lot have a common currency */
202  if (nullptr == currency)
203  {
204  currency = trans->common_currency;
205  }
206  if (FALSE == gnc_commodity_equiv (currency, trans->common_currency))
207  {
208  /* This lot has mixed currencies. Can't double-balance.
209  * Silently punt */
210  PWARN ("Lot with multiple currencies:\n"
211  "\ttrans=%s curr=%s", xaccTransGetDescription(trans),
212  gnc_commodity_get_fullname(trans->common_currency));
213  break;
214  }
215 
216  /* Now, total up the values */
217  value = gnc_numeric_add (value, xaccSplitGetValue (s),
219  PINFO ("Split=%p value=%s Accum Lot value=%s", s,
220  gnc_num_dbg_to_string (s->value),
221  gnc_num_dbg_to_string (value));
222 
223  }
224 
225  if (FALSE == gnc_numeric_equal (value, zero))
226  {
227  /* Unhandled error condition. Not sure what to do here,
228  * Since the ComputeCapGains should have gotten it right.
229  * I suppose there might be small rounding errors, a penny or two,
230  * the ideal thing would to figure out why there's a rounding
231  * error, and fix that.
232  */
233  PERR ("Closed lot fails to double-balance !! lot value=%s",
234  gnc_num_dbg_to_string (value));
235  for (node = gnc_lot_get_split_list(lot); node; node = node->next)
236  {
237  Split *s = GNC_SPLIT(node->data);
238  PERR ("s=%p amt=%s val=%s", s,
239  gnc_num_dbg_to_string(s->amount),
240  gnc_num_dbg_to_string(s->value));
241  }
242  }
243 
244  LEAVE ("lot=%s", gnc_lot_get_title(lot));
245 }
246 
247 /* ================================================================= */
248 
249 static inline gboolean
250 is_subsplit (Split *split)
251 {
252 
253  /* generic stop-progress conditions */
254  if (!split) return FALSE;
255  g_return_val_if_fail (split->parent, FALSE);
256 
257  /* If there are no sub-splits, then there's nothing to do. */
258  return xaccSplitHasPeers (split);
259 }
260 
261 /* ================================================================= */
262 
263 
264 /* ================================================================= */
265 
266 /* Remove the guid of b from a. Note that a may not contain the guid
267  * of b, (and v.v.) in which case, it will contain other guids which
268  * establish the links. So merge them back in. */
269 
270 static void
271 remove_guids (Split *sa, Split *sb)
272 {
273  xaccSplitRemovePeerSplit (sa, sb);
274  xaccSplitRemovePeerSplit (sb, sa);
275  xaccSplitMergePeerSplits (sa, sb);
276 }
277 
278 /* The merge_splits() routine causes the amount & value of sb
279  * to be merged into sa; it then destroys sb. It also performs
280  * some other misc cleanup */
281 
282 static void
283 merge_splits (Split *sa, Split *sb)
284 {
285  Account *act;
286  Transaction *txn;
287  gnc_numeric amt, val;
288 
289  act = xaccSplitGetAccount (sb);
290  xaccAccountBeginEdit (act);
291 
292  txn = sa->parent;
293  xaccTransBeginEdit (txn);
294 
295  /* Remove the guid of sb from the 'gemini' of sa */
296  remove_guids (sa, sb);
297 
298  /* Add amount of sb into sa, ditto for value. */
299  amt = xaccSplitGetAmount (sa);
300  amt = gnc_numeric_add_fixed (amt, xaccSplitGetAmount (sb));
301  xaccSplitSetAmount (sa, amt);
302 
303  val = xaccSplitGetValue (sa);
304  val = gnc_numeric_add_fixed (val, xaccSplitGetValue (sb));
305  xaccSplitSetValue (sa, val);
306 
307  /* Set reconcile to no; after this much violence,
308  * no way its reconciled. */
310 
311  /* If sb has associated gains splits, trash them. */
312  if ((sb->gains_split) &&
313  (sb->gains_split->gains & GAINS_STATUS_GAINS))
314  {
315  Transaction *t = sb->gains_split->parent;
316  xaccTransBeginEdit (t);
317  xaccTransDestroy (t);
319  }
320 
321  /* Finally, delete sb */
322  xaccSplitDestroy(sb);
323 
324  xaccTransCommitEdit (txn);
325  xaccAccountCommitEdit (act);
326 }
327 
328 gboolean
329 xaccScrubMergeSubSplits (Split *split, gboolean strict)
330 {
331  gboolean rc = FALSE;
332  Transaction *txn;
333  SplitList *node;
334  GNCLot *lot;
335 
336  if (strict && (FALSE == is_subsplit (split))) return FALSE;
337 
338  txn = split->parent;
339 
340  // Don't mess with splits from an invoice transaction
341  // Those are the responsibility of the business code
342  if (gncInvoiceGetInvoiceFromTxn (txn)) return FALSE;
343 
344  lot = xaccSplitGetLot (split);
345 
346  ENTER ("(Lot=%s)", gnc_lot_get_title(lot));
347 restart:
348  for (node = txn->splits; node; node = node->next)
349  {
350  Split *s = GNC_SPLIT(node->data);
351  if (xaccSplitGetLot (s) != lot) continue;
352  if (s == split) continue;
353  if (qof_instance_get_destroying(s)) continue;
354 
355  // Don't mess with splits from an invoice transaction
356  // Those are the responsibility of the business code
357  if (gncInvoiceGetInvoiceFromTxn (s->parent)) return FALSE;
358 
359  if (strict)
360  {
361  /* OK, this split is in the same lot (and thus same account)
362  * as the indicated split. Make sure it is really a subsplit
363  * of the split we started with. It's possible to have two
364  * splits in the same lot and transaction that are not subsplits
365  * of each other, the test-period test suite does this, for
366  * example. Only worry about adjacent sub-splits. By
367  * repeatedly merging adjacent subsplits, we'll get the non-
368  * adjacent ones too. */
369  if (!xaccSplitIsPeerSplit (split, s))
370  continue;
371  }
372 
373  merge_splits (split, s);
374  rc = TRUE;
375  goto restart;
376  }
377  if (rc && gnc_numeric_zero_p (split->amount))
378  {
379  time64 pdate = xaccTransGetDate (txn);
380  gchar *pdatestr = gnc_ctime (&pdate);
381  PWARN ("Result of merge has zero amt!");
382  PWARN ("Transaction details - posted date %s - description %s", pdatestr, xaccTransGetDescription(txn));
383  g_free (pdatestr);
384  }
385  LEAVE (" splits merged=%d", rc);
386  return rc;
387 }
388 
389 gboolean
390 xaccScrubMergeLotSubSplits (GNCLot *lot, gboolean strict)
391 {
392  gboolean rc = FALSE;
393  SplitList *node;
394 
395  if (!lot) return FALSE;
396 
397  ENTER (" ");
398 restart:
399  for (node = gnc_lot_get_split_list(lot); node; node = node->next)
400  {
401  Split *s = GNC_SPLIT(node->data);
402  if (!xaccScrubMergeSubSplits(s, strict)) continue;
403 
404  rc = TRUE;
405  goto restart;
406  }
407  LEAVE (" splits merged=%d", rc);
408  return rc;
409 }
410 
411 /* =========================== 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
This is the private header for the account structure.
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.cpp:390
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.cpp:95
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
gboolean xaccSplitDestroy(Split *split)
Destructor.
Definition: Split.cpp:1506
Utilities to Convert Stock Accounts to use Lots.
STRUCTS.
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.cpp:522
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.cpp:59
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
void xaccSplitSetReconcile(Split *split, char recn)
Set the reconcile flag.
Use any denominator which gives an exactly correct ratio of numerator to denominator.
Definition: gnc-numeric.h:188
void xaccSplitRemovePeerSplit(Split *split, const Split *other_split)
Remove a peer split from this split&#39;s lot-split list.
Definition: Split.cpp:2121
#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:1272
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.cpp:2102
void xaccSplitMergePeerSplits(Split *split, const Split *other_split)
Merge the other_split&#39;s peer splits into split&#39;s peers.
Definition: Split.cpp:2138
GList SplitList
GList of Split.
Definition: gnc-engine.h:207
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.
Account public routines (C++ api)
Implement Accounting Policy Private header File.
SplitList * gnc_lot_get_split_list(const GNCLot *lot)
Returns a list of all the splits in this lot.
Definition: gnc-lot.cpp:425
void xaccLotScrubDoubleBalance(GNCLot *lot)
The xaccLotScrubDoubleBalance() routine examines the indicated lot.
Definition: Scrub2.cpp:171
gboolean xaccSplitIsPeerSplit(const Split *split, const Split *other_split)
Report if a split is a peer of this one.
Definition: Split.cpp:2108
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.cpp:329
gboolean xaccSplitIsStockSplit(Split *s)
Returns true if the split is of type stock split.
Definition: Split.cpp:2078
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.cpp:219
gboolean xaccTransGetVoidStatus(const Transaction *trans)
Retrieve information on whether or not a transaction has been voided.
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.cpp:436
gboolean gnc_lot_is_closed(GNCLot *lot)
Returns closed status of the given lot.
Definition: gnc-lot.cpp:367
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:1475
Account * xaccSplitGetAccount(const Split *split)
Returns the account of this split, which was set through xaccAccountInsertSplit().
Definition: gmock-Split.cpp:53
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition: gnc-date.h:87
Account * gnc_lot_get_account(const GNCLot *lot)
Returns the account with which this lot is associated.
Definition: gnc-lot.cpp:377
char * gnc_ctime(const time64 *secs)
Return a string representation of a date from a 64-bit time value.
Definition: gnc-date.cpp:256
const char * xaccAccountGetName(const Account *acc)
Get the account&#39;s name.
Definition: Account.cpp:3289
#define GNC_DENOM_AUTO
Values that can be passed as the &#39;denom&#39; argument.
Definition: gnc-numeric.h:245
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:1516
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:2100
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)
Returns the lot balance.
Definition: gnc-lot.cpp:487
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.cpp:1920
#define NREC
not reconciled or cleared
Definition: Split.h:76
gnc_numeric xaccSplitGetAmount(const Split *split)
Returns the amount of the split in the account&#39;s commodity.
Definition: gmock-Split.cpp:69