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 voided transactions */
73  if (gnc_numeric_zero_p (split->amount) &&
74  xaccTransGetVoidStatus(split->parent)) continue;
75 
76  if (xaccSplitAssign (split)) goto restart_loop;
77  }
79  LEAVE ("acc=%s", xaccAccountGetName(acc));
80 }
81 
82 /* ============================================================== */
83 
91 void
92 xaccLotFill (GNCLot *lot)
93 {
94  Account *acc;
95  Split *split;
96  GNCPolicy *pcy;
97 
98  if (!lot) return;
99  acc = gnc_lot_get_account(lot);
100  pcy = gnc_account_get_policy(acc);
101 
102  ENTER ("(lot=%s, acc=%s)", gnc_lot_get_title(lot), xaccAccountGetName(acc));
103 
104  /* If balance already zero, we have nothing to do. */
105  if (gnc_lot_is_closed (lot))
106  {
107  LEAVE ("Lot Closed (lot=%s, acc=%s)", gnc_lot_get_title(lot),
108  xaccAccountGetName(acc));
109  return;
110  }
111  split = pcy->PolicyGetSplit (pcy, lot);
112  if (!split)
113  {
114  LEAVE ("No Split (lot=%s, acc=%s)", gnc_lot_get_title(lot),
115  xaccAccountGetName(acc));
116  return; /* Handle the common case */
117  }
118 
119  /* Reject voided transactions */
120  if (gnc_numeric_zero_p(split->amount) &&
121  xaccTransGetVoidStatus(split->parent))
122  {
123  LEAVE ("Voided transaction (lot=%s, acc=%s)",
124  gnc_lot_get_title(lot), xaccAccountGetName(acc));
125  return;
126  }
127 
128  xaccAccountBeginEdit (acc);
129 
130  /* Loop until we've filled up the lot, (i.e. till the
131  * balance goes to zero) or there are no splits left. */
132  while (1)
133  {
134  Split *subsplit;
135 
136  subsplit = xaccSplitAssignToLot (split, lot);
137  if (subsplit == split)
138  {
139  PERR ("Accounting Policy gave us a split that "
140  "doesn't fit into this lot\n"
141  "lot baln=%s, isclosed=%d, aplit amt=%s",
143  gnc_lot_is_closed (lot),
144  gnc_num_dbg_to_string (split->amount));
145  break;
146  }
147 
148  if (gnc_lot_is_closed (lot)) break;
149 
150  split = pcy->PolicyGetSplit (pcy, lot);
151  if (!split) break;
152  }
153  xaccAccountCommitEdit (acc);
154  LEAVE ("(lot=%s, acc=%s)", gnc_lot_get_title(lot), xaccAccountGetName(acc));
155 }
156 
157 /* ============================================================== */
158 
159 void
161 {
162  gnc_commodity *currency = nullptr;
163  SplitList *snode;
164  GList *node;
165  gnc_numeric zero = gnc_numeric_zero();
166  gnc_numeric value = zero;
167 
168  if (!lot) return;
169 
170  ENTER ("lot=%s", gnc_lot_get_title(lot));
171 
172  for (snode = gnc_lot_get_split_list(lot); snode; snode = snode->next)
173  {
174  Split *s = GNC_SPLIT(snode->data);
175  xaccSplitComputeCapGains (s, nullptr);
176  }
177 
178  /* We double-check only closed lots */
179  if (FALSE == gnc_lot_is_closed (lot))
180  {
181  LEAVE ("lot=%s is closed", gnc_lot_get_title(lot));
182  return;
183  }
184 
185  for (snode = gnc_lot_get_split_list(lot); snode; snode = snode->next)
186  {
187  Split *s = GNC_SPLIT(snode->data);
188  Transaction *trans = s->parent;
189 
190  /* Check to make sure all splits in the lot have a common currency */
191  if (nullptr == currency)
192  {
193  currency = trans->common_currency;
194  }
195  if (FALSE == gnc_commodity_equiv (currency, trans->common_currency))
196  {
197  /* This lot has mixed currencies. Can't double-balance.
198  * Silently punt */
199  PWARN ("Lot with multiple currencies:\n"
200  "\ttrans=%s curr=%s", xaccTransGetDescription(trans),
201  gnc_commodity_get_fullname(trans->common_currency));
202  break;
203  }
204 
205  /* Now, total up the values */
206  value = gnc_numeric_add (value, xaccSplitGetValue (s),
208  PINFO ("Split=%p value=%s Accum Lot value=%s", s,
209  gnc_num_dbg_to_string (s->value),
210  gnc_num_dbg_to_string (value));
211 
212  }
213 
214  if (FALSE == gnc_numeric_equal (value, zero))
215  {
216  /* Unhandled error condition. Not sure what to do here,
217  * Since the ComputeCapGains should have gotten it right.
218  * I suppose there might be small rounding errors, a penny or two,
219  * the ideal thing would to figure out why there's a rounding
220  * error, and fix that.
221  */
222  PERR ("Closed lot fails to double-balance !! lot value=%s",
223  gnc_num_dbg_to_string (value));
224  for (node = gnc_lot_get_split_list(lot); node; node = node->next)
225  {
226  Split *s = GNC_SPLIT(node->data);
227  PERR ("s=%p amt=%s val=%s", s,
228  gnc_num_dbg_to_string(s->amount),
229  gnc_num_dbg_to_string(s->value));
230  }
231  }
232 
233  LEAVE ("lot=%s", gnc_lot_get_title(lot));
234 }
235 
236 /* ================================================================= */
237 
238 static inline gboolean
239 is_subsplit (Split *split)
240 {
241 
242  /* generic stop-progress conditions */
243  if (!split) return FALSE;
244  g_return_val_if_fail (split->parent, FALSE);
245 
246  /* If there are no sub-splits, then there's nothing to do. */
247  return xaccSplitHasPeers (split);
248 }
249 
250 /* ================================================================= */
251 
252 
253 /* ================================================================= */
254 
255 /* Remove the guid of b from a. Note that a may not contain the guid
256  * of b, (and v.v.) in which case, it will contain other guids which
257  * establish the links. So merge them back in. */
258 
259 static void
260 remove_guids (Split *sa, Split *sb)
261 {
262  xaccSplitRemovePeerSplit (sa, sb);
263  xaccSplitRemovePeerSplit (sb, sa);
264  xaccSplitMergePeerSplits (sa, sb);
265 }
266 
267 /* The merge_splits() routine causes the amount & value of sb
268  * to be merged into sa; it then destroys sb. It also performs
269  * some other misc cleanup */
270 
271 static void
272 merge_splits (Split *sa, Split *sb)
273 {
274  Account *act;
275  Transaction *txn;
276  gnc_numeric amt, val;
277 
278  act = xaccSplitGetAccount (sb);
279  xaccAccountBeginEdit (act);
280 
281  txn = sa->parent;
282  xaccTransBeginEdit (txn);
283 
284  /* Remove the guid of sb from the 'gemini' of sa */
285  remove_guids (sa, sb);
286 
287  /* Add amount of sb into sa, ditto for value. */
288  amt = xaccSplitGetAmount (sa);
289  amt = gnc_numeric_add_fixed (amt, xaccSplitGetAmount (sb));
290  xaccSplitSetAmount (sa, amt);
291 
292  val = xaccSplitGetValue (sa);
293  val = gnc_numeric_add_fixed (val, xaccSplitGetValue (sb));
294  xaccSplitSetValue (sa, val);
295 
296  /* Set reconcile to no; after this much violence,
297  * no way its reconciled. */
299 
300  /* If sb has associated gains splits, trash them. */
301  if ((sb->gains_split) &&
302  (sb->gains_split->gains & GAINS_STATUS_GAINS))
303  {
304  Transaction *t = sb->gains_split->parent;
305  xaccTransBeginEdit (t);
306  xaccTransDestroy (t);
308  }
309 
310  /* Finally, delete sb */
311  xaccSplitDestroy(sb);
312 
313  xaccTransCommitEdit (txn);
314  xaccAccountCommitEdit (act);
315 }
316 
317 gboolean
318 xaccScrubMergeSubSplits (Split *split, gboolean strict)
319 {
320  gboolean rc = FALSE;
321  Transaction *txn;
322  SplitList *node;
323  GNCLot *lot;
324 
325  if (strict && (FALSE == is_subsplit (split))) return FALSE;
326 
327  txn = split->parent;
328 
329  // Don't mess with splits from an invoice transaction
330  // Those are the responsibility of the business code
331  if (gncInvoiceGetInvoiceFromTxn (txn)) return FALSE;
332 
333  lot = xaccSplitGetLot (split);
334 
335  ENTER ("(Lot=%s)", gnc_lot_get_title(lot));
336 restart:
337  for (node = txn->splits; node; node = node->next)
338  {
339  Split *s = GNC_SPLIT(node->data);
340  if (xaccSplitGetLot (s) != lot) continue;
341  if (s == split) continue;
342  if (qof_instance_get_destroying(s)) continue;
343 
344  // Don't mess with splits from an invoice transaction
345  // Those are the responsibility of the business code
346  if (gncInvoiceGetInvoiceFromTxn (s->parent)) return FALSE;
347 
348  if (strict)
349  {
350  /* OK, this split is in the same lot (and thus same account)
351  * as the indicated split. Make sure it is really a subsplit
352  * of the split we started with. It's possible to have two
353  * splits in the same lot and transaction that are not subsplits
354  * of each other, the test-period test suite does this, for
355  * example. Only worry about adjacent sub-splits. By
356  * repeatedly merging adjacent subsplits, we'll get the non-
357  * adjacent ones too. */
358  if (!xaccSplitIsPeerSplit (split, s))
359  continue;
360  }
361 
362  merge_splits (split, s);
363  rc = TRUE;
364  goto restart;
365  }
366  if (rc && gnc_numeric_zero_p (split->amount))
367  {
368  time64 pdate = xaccTransGetDate (txn);
369  gchar *pdatestr = gnc_ctime (&pdate);
370  PWARN ("Result of merge has zero amt!");
371  PWARN ("Transaction details - posted date %s - description %s", pdatestr, xaccTransGetDescription(txn));
372  g_free (pdatestr);
373  }
374  LEAVE (" splits merged=%d", rc);
375  return rc;
376 }
377 
378 gboolean
379 xaccScrubMergeLotSubSplits (GNCLot *lot, gboolean strict)
380 {
381  gboolean rc = FALSE;
382  SplitList *node;
383 
384  if (!lot) return FALSE;
385 
386  ENTER (" ");
387 restart:
388  for (node = gnc_lot_get_split_list(lot); node; node = node->next)
389  {
390  Split *s = GNC_SPLIT(node->data);
391  if (!xaccScrubMergeSubSplits(s, strict)) continue;
392 
393  rc = TRUE;
394  goto restart;
395  }
396  LEAVE (" splits merged=%d", rc);
397  return rc;
398 }
399 
400 /* =========================== 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:379
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:92
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
gboolean xaccSplitDestroy(Split *split)
Destructor.
Definition: Split.cpp:1471
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:526
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:2051
#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:1327
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:2032
void xaccSplitMergePeerSplits(Split *split, const Split *other_split)
Merge the other_split&#39;s peer splits into split&#39;s peers.
Definition: Split.cpp:2068
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:160
gboolean xaccSplitIsPeerSplit(const Split *split, const Split *other_split)
Report if a split is a peer of this one.
Definition: Split.cpp:2038
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:318
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:426
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:1479
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:255
const char * xaccAccountGetName(const Account *acc)
Get the account&#39;s name.
Definition: Account.cpp:3248
#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:1520
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:2098
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:502
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:1882
#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