GnuCash  4.8a-132-gcdaeb421d+
ScrubBusiness.c
1 /********************************************************************\
2  * ScrubBusiness.h -- Cleanup functions for the business objects. *
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 
30 #include <config.h>
31 
32 #include <glib.h>
33 #include <glib/gi18n.h>
34 
35 #include "gnc-engine.h"
36 #include "gnc-lot.h"
37 #include "policy-p.h"
38 #include "Account.h"
39 #include "gncInvoice.h"
40 #include "gncInvoiceP.h"
41 #include "Scrub.h"
42 #include "Scrub2.h"
43 #include "ScrubBusiness.h"
44 #include "Transaction.h"
45 
46 #undef G_LOG_DOMAIN
47 #define G_LOG_DOMAIN "gnc.engine.scrub"
48 
49 static QofLogModule log_module = G_LOG_DOMAIN;
50 
51 static void
52 gncScrubInvoiceState (GNCLot *lot)
53 {
54  SplitList *ls_iter = NULL;
55  GncInvoice *invoice = NULL;
56  GncInvoice *lot_invoice = gncInvoiceGetInvoiceFromLot (lot);
57 
58  for (ls_iter = gnc_lot_get_split_list (lot); ls_iter; ls_iter = ls_iter->next)
59  {
60  Split *split = ls_iter->data;
61  Transaction *txn = NULL; // ll_txn = "Lot Link Transaction"
62 
63  if (!split)
64  continue; // next scrub lot split
65 
66  txn = xaccSplitGetParent (split);
67  invoice = gncInvoiceGetInvoiceFromTxn (txn);
68  if (invoice)
69  break;
70 
71  }
72 
73  if (invoice != lot_invoice)
74  {
75  PINFO("Correcting lot invoice associaton. Old invoice: %p, new invoice %p", lot_invoice, invoice);
76  gncInvoiceDetachFromLot(lot);
77 
78  if (invoice)
79  gncInvoiceAttachToLot (invoice, lot);
80  else
81  gncOwnerAttachToLot (gncInvoiceGetOwner(lot_invoice), lot);
82  }
83 }
84 
85 // A helper function that takes two splits. If the splits are of opposite sign
86 // it reduces the biggest split to have the same value (but with opposite sign)
87 // of the smaller split.
88 // To make sure everything still continues to balance in addition a "remainder" split
89 // will be created that will be added to the same lot and transaction as the biggest
90 // split.
91 // The opposite sign restriction is because that's the only scenario that makes sense
92 // in the context of scrubbing business lots below.
93 // If we created new splits, return TRUE, otherwise FALSE
94 static gboolean reduce_biggest_split (Split *splitA, Split *splitB)
95 {
96  gnc_numeric valA = xaccSplitGetValue (splitA);
97  gnc_numeric valB = xaccSplitGetValue (splitB);
98 
99  if (gnc_numeric_compare (gnc_numeric_abs (valA), gnc_numeric_abs (valB)) >= 0)
100  return gncOwnerReduceSplitTo (splitA, gnc_numeric_neg (valB));
101  else
102  return gncOwnerReduceSplitTo (splitB, gnc_numeric_neg (valA));
103 }
104 
105 // Attempt to eliminate or reduce the lot link splits (ll_*_split)
106 // between from_lot and to_lot. To do so this function will attempt
107 // to move a payment split from from_lot to to_lot in order to
108 // balance the lot link split that will be deleted.
109 // To ensure everything remains balanced at most
110 // min (val-ll-*-split, val-pay-split) (in absolute values) can be moved.
111 // If any split involved has a larger value, it will be split in two
112 // and only the part matching the other splits' value will be used.
113 // The leftover splits are kept in the respective transactions/lots.
114 // A future scrub action can still act on those if needed.
115 //
116 // Note that this function assumes that ll_from_split and ll_to_split are
117 // of opposite sign. The calling function should check this.
118 
119 static gboolean
120 scrub_other_link (GNCLot *from_lot, Split *ll_from_split,
121  GNCLot *to_lot, Split *ll_to_split)
122 {
123  Split *real_from_split; // This refers to the split in the payment lot representing the payment itself
124  gboolean modified = FALSE;
125  gnc_numeric real_from_val;
126  gnc_numeric from_val = xaccSplitGetValue (ll_from_split);
127  gnc_numeric to_val = xaccSplitGetValue (ll_to_split);
128  Transaction *ll_txn = xaccSplitGetParent (ll_to_split);
129 
130  // Per iteration we can only scrub at most min (val-doc-split, val-pay-split)
131  // So set the ceiling for finding a potential offsetting split in the lot
132  if (gnc_numeric_compare (gnc_numeric_abs (from_val), gnc_numeric_abs (to_val)) >= 0)
133  from_val = gnc_numeric_neg (to_val);
134 
135  // Next we have to find the original payment split so we can
136  // add (part of) it to the document lot
137  real_from_split = gncOwnerFindOffsettingSplit (from_lot, from_val);
138  if (!real_from_split)
139  return FALSE; // No usable split in the payment lot
140 
141  // We now have found 3 splits involved in the scrub action:
142  // 2 lot link splits which we want to reduce
143  // 1 other split to move into the original lot instead of the lot link split
144  // As said only value of the split can be offset.
145  // So split the bigger ones in two if needed and continue with equal valued splits only
146  // The remainder is added to the lot link transaction and the lot to keep everything balanced
147  // and will be processed in a future iteration
148  modified = reduce_biggest_split (ll_from_split, ll_to_split);
149  modified |= reduce_biggest_split (real_from_split, ll_from_split);
150  modified |= reduce_biggest_split (ll_from_split, ll_to_split);
151 
152  // At this point ll_to_split and real_from_split should have the same value
153  // If not, flag a warning and skip to the next iteration
154  to_val = xaccSplitGetValue (ll_to_split);
155  real_from_val = xaccSplitGetValue (real_from_split);
156  if (!gnc_numeric_equal (real_from_val, to_val))
157  {
158  // This is unexpected - write a warning message and skip this split
159  PWARN("real_from_val (%s) and to_val (%s) differ. "
160  "This is unexpected! Skip scrubbing of real_from_split %p against ll_to_split %p.",
161  gnc_numeric_to_string (real_from_val), // gnc_numeric_denom (real_from_val),
162  gnc_numeric_to_string (to_val), // gnc_numeric_denom (to_val),
163  real_from_split, ll_to_split);
164  return modified;
165  }
166 
167  // Now do the actual split dance
168  // - move real payment split to doc lot
169  // - delete both lot link splits from the lot link transaction
170  gnc_lot_add_split (to_lot, real_from_split);
171  xaccTransBeginEdit (ll_txn);
172  xaccSplitDestroy (ll_to_split);
173  xaccSplitDestroy (ll_from_split);
174  xaccTransCommitEdit (ll_txn);
175 
176  // Cleanup the lots
177  xaccScrubMergeLotSubSplits (to_lot, FALSE);
178  xaccScrubMergeLotSubSplits (from_lot, FALSE);
179 
180  return TRUE; // We did change splits/transactions/lots...
181 }
182 
183 static gboolean
184 gncScrubLotLinks (GNCLot *scrub_lot)
185 {
186  gboolean modified = FALSE, restart_needed = FALSE;
187  SplitList *sls_iter = NULL;
188 
189 scrub_start:
190  restart_needed = FALSE;
191 
192  // Iterate over all splits in the lot
193  for (sls_iter = gnc_lot_get_split_list (scrub_lot); sls_iter; sls_iter = sls_iter->next)
194  {
195  Split *sl_split = sls_iter->data;
196  Transaction *ll_txn = NULL; // ll_txn = "Lot Link Transaction"
197  SplitList *lts_iter = NULL;
198 
199  if (!sl_split)
200  continue; // next scrub lot split
201 
202  ll_txn = xaccSplitGetParent (sl_split);
203 
204  if (!ll_txn)
205  {
206  // Ooops - the split doesn't belong to any transaction !
207  // This is not expected so issue a warning and continue with next split
208  PWARN("Encountered a split in a business lot that's not part of any transaction. "
209  "This is unexpected! Skipping split %p.", sl_split);
210  continue;
211  }
212 
213  // Don't scrub invoice type transactions
214  if (xaccTransGetTxnType (ll_txn) == TXN_TYPE_INVOICE)
215  continue; // next scrub lot split
216 
217  // Empty splits can be removed immediately
218  if (gnc_numeric_zero_p (xaccSplitGetValue (sl_split)) ||
220  {
221  xaccSplitDestroy (sl_split);
222  modified = TRUE;
223  goto scrub_start;
224  }
225 
226  // Iterate over all splits in the lot link transaction
227  for (lts_iter = xaccTransGetSplitList (ll_txn); lts_iter; lts_iter = lts_iter->next)
228  {
229  Split *ll_txn_split = lts_iter->data; // These all refer to splits in the lot link transaction
230  GNCLot *remote_lot = NULL; // lot at the other end of the lot link transaction
231  gboolean sl_is_doc_lot, rl_is_doc_lot;
232 
233  if (!ll_txn_split)
234  continue; // next lot link transaction split
235 
236  // Skip the split in the lot we're currently scrubbing
237  if (sl_split == ll_txn_split)
238  continue; // next lot link transaction split
239 
240  // Skip empty other splits. They'll be scrubbed in the outer for loop later
241  if (gnc_numeric_zero_p (xaccSplitGetValue (ll_txn_split)) ||
242  gnc_numeric_zero_p(xaccSplitGetValue (ll_txn_split)))
243  continue;
244 
245  // Only splits of opposite signed values can be scrubbed
246  if (gnc_numeric_positive_p (xaccSplitGetValue (sl_split)) ==
247  gnc_numeric_positive_p (xaccSplitGetValue (ll_txn_split)))
248  continue; // next lot link transaction split
249 
250  // We can only scrub if the other split is in a lot as well
251  // Link transactions always have their other split in another lot
252  // however ordinary payment transactions may not
253  remote_lot = xaccSplitGetLot (ll_txn_split);
254  if (!remote_lot)
255  continue;
256 
257  sl_is_doc_lot = (gncInvoiceGetInvoiceFromLot (scrub_lot) != NULL);
258  rl_is_doc_lot = (gncInvoiceGetInvoiceFromLot (remote_lot) != NULL);
259 
260  // Depending on the type of lots we're comparing, we need different actions
261  // - Two document lots (an invoice and a credit note):
262  // Special treatment - look for all document lots linked via ll_txn
263  // and update the memo to be of more use to the users.
264  // - Two payment lots:
265  // (Part of) the link will be eliminated and instead (part of)
266  // one payment will be added to the other lot to keep the balance.
267  // If the payments are not equal in abs value part of the bigger payment
268  // will be moved to the smaller payment's lot.
269  // - A document and a payment lot:
270  // (Part of) the link will be eliminated and instead (part of) the real
271  // payment will be added to the document lot to handle the payment.
272  if (sl_is_doc_lot && rl_is_doc_lot)
273  gncOwnerSetLotLinkMemo (ll_txn);
274  else if (!sl_is_doc_lot && !rl_is_doc_lot)
275  {
276  gint cmp = gnc_numeric_compare (gnc_numeric_abs (xaccSplitGetValue (sl_split)),
277  gnc_numeric_abs (xaccSplitGetValue (ll_txn_split)));
278  if (cmp >= 0)
279  restart_needed = scrub_other_link (scrub_lot, sl_split, remote_lot, ll_txn_split);
280  else
281  restart_needed = scrub_other_link (remote_lot, ll_txn_split, scrub_lot, sl_split);
282  }
283  else
284  {
285  GNCLot *doc_lot = sl_is_doc_lot ? scrub_lot : remote_lot;
286  GNCLot *pay_lot = sl_is_doc_lot ? remote_lot : scrub_lot;
287  Split *ll_doc_split = sl_is_doc_lot ? sl_split : ll_txn_split;
288  Split *ll_pay_split = sl_is_doc_lot ? ll_txn_split : sl_split;
289  // Ok, let's try to move a payment from pay_lot to doc_lot
290  restart_needed = scrub_other_link (pay_lot, ll_pay_split, doc_lot, ll_doc_split);
291  }
292 
293  // If we got here, the splits in our lot and ll_txn have been severely mixed up
294  // And our iterator lists are probably no longer valid
295  // So let's start over
296  if (restart_needed)
297  {
298  modified = TRUE;
299  goto scrub_start;
300  }
301 
302  }
303  }
304 
305  return modified;
306 }
307 
308 // Note this is a recursive function. It presumes the number of splits
309 // in avail_splits is relatively low. With many splits the performance will
310 // quickly degrade.
311 // Careful: this function assumes all splits in avail_splits to be valid
312 // and with values of opposite sign of target_value
313 // Ignoring this can cause unexpected results!
314 static SplitList *
315 gncSLFindOffsSplits (SplitList *avail_splits, gnc_numeric target_value)
316 {
317  gint curr_recurse_level = 0;
318  gint max_recurse_level = g_list_length (avail_splits) - 1;
319 
320  if (!avail_splits)
321  return NULL;
322 
323  for (curr_recurse_level = 0;
324  curr_recurse_level <= max_recurse_level;
325  curr_recurse_level++)
326  {
327  SplitList *split_iter = NULL;
328  for (split_iter = avail_splits; split_iter; split_iter = split_iter->next)
329  {
330  Split *split = split_iter->data;
331  SplitList *match_splits = NULL;
332  gnc_numeric split_value, remaining_value;
333 
334  split_value = xaccSplitGetValue (split);
335  // Attention: target_value and split_value are of opposite sign
336  // So to get the remaining target value, they should be *added*
337  remaining_value = gnc_numeric_add (target_value, split_value,
339 
340  if (curr_recurse_level == 0)
341  {
342  if (gnc_numeric_zero_p (remaining_value))
343  match_splits = g_list_prepend (NULL, split);
344  }
345  else
346  {
347  if (gnc_numeric_positive_p (target_value) ==
348  gnc_numeric_positive_p (remaining_value))
349  match_splits = gncSLFindOffsSplits (split_iter->next,
350  remaining_value);
351  }
352 
353  if (match_splits)
354  return g_list_prepend (match_splits, split);
355  }
356  }
357 
358  return NULL;
359 }
360 
361 
362 static gboolean
363 gncScrubLotDanglingPayments (GNCLot *lot)
364 {
365  SplitList * split_list, *filtered_list = NULL, *match_list = NULL, *node;
366  Split *ll_split = gnc_lot_get_earliest_split (lot);
367  Transaction *ll_trans = xaccSplitGetParent (ll_split);
368  gnc_numeric ll_val = xaccSplitGetValue (ll_split);
369  time64 ll_date = xaccTransGetDate (ll_trans);
370  const char *ll_desc = xaccTransGetDescription (ll_trans);
371 
372  // look for free splits (i.e. not in any lot) which,
373  // compared to the lot link split
374  // - have the same date
375  // - have the same description
376  // - have an opposite sign amount
377  // - free split's abs value is less than or equal to ll split's abs value
378  split_list = xaccAccountGetSplitList(gnc_lot_get_account (lot));
379  for (node = split_list; node; node = node->next)
380  {
381  Split *free_split = node->data;
382  Transaction *free_trans;
383  gnc_numeric free_val;
384 
385  if (NULL != xaccSplitGetLot(free_split))
386  continue;
387 
388  free_trans = xaccSplitGetParent (free_split);
389  if (ll_date != xaccTransGetDate (free_trans))
390  continue;
391 
392  if (0 != g_strcmp0 (ll_desc, xaccTransGetDescription (free_trans)))
393  continue;
394 
395  free_val = xaccSplitGetValue (free_split);
396  if (gnc_numeric_positive_p (ll_val) ==
397  gnc_numeric_positive_p (free_val))
398  continue;
399 
400  if (gnc_numeric_compare (gnc_numeric_abs (free_val), gnc_numeric_abs (ll_val)) > 0)
401  continue;
402 
403  filtered_list = g_list_prepend (filtered_list, free_split);
404  }
405 
406  filtered_list = g_list_reverse (filtered_list);
407  match_list = gncSLFindOffsSplits (filtered_list, ll_val);
408  g_list_free (filtered_list);
409 
410  for (node = match_list; node; node = node->next)
411  {
412  Split *match_split = node->data;
413  gnc_lot_add_split (lot, match_split);
414  }
415 
416  if (match_list)
417  {
418  g_list_free (match_list);
419  return TRUE;
420  }
421  else
422  return FALSE;
423 }
424 
425 static gboolean
426 gncScrubLotIsSingleLotLinkSplit (GNCLot *lot)
427 {
428  Split *split = NULL;
429  Transaction *trans = NULL;
430 
431  // Lots with a single split which is also a lot link transaction split
432  // may be sign of a dangling payment. Let's try to fix that
433 
434  // Only works for single split lots...
435  if (1 != gnc_lot_count_splits (lot))
436  return FALSE;
437 
438  split = gnc_lot_get_earliest_split (lot);
439  trans = xaccSplitGetParent (split);
440 
441  if (!trans)
442  {
443  // Ooops - the split doesn't belong to any transaction !
444  // This is not expected so issue a warning and continue with next split
445  PWARN("Encountered a split in a business lot that's not part of any transaction. "
446  "This is unexpected! Skipping split %p.", split);
447  return FALSE;
448  }
449 
450  // Only works if single split belongs to a lot link transaction...
451  if (xaccTransGetTxnType (trans) != TXN_TYPE_LINK)
452  return FALSE;
453 
454  return TRUE;
455 }
456 
457 gboolean
458 gncScrubBusinessLot (GNCLot *lot)
459 {
460  gboolean splits_deleted = FALSE;
461  gboolean dangling_payments = FALSE;
462  gboolean dangling_lot_link = FALSE;
463  Account *acc;
464  gchar *lotname=NULL;
465 
466  if (!lot) return FALSE;
467  lotname = g_strdup (gnc_lot_get_title (lot));
468  ENTER ("(lot=%p) %s", lot, lotname ? lotname : "(no lotname)");
469 
470  acc = gnc_lot_get_account (lot);
471  if (acc)
473 
474  /* Check invoice link consistency
475  * A lot should have both or neither of:
476  * - one split from an invoice transaction
477  * - an invoice-guid set
478  */
479  gncScrubInvoiceState (lot);
480 
481  // Scrub lot links.
482  // They should only remain when two document lots are linked together
483  xaccScrubMergeLotSubSplits (lot, FALSE);
484  splits_deleted = gncScrubLotLinks (lot);
485 
486  // Look for dangling payments and repair if found
487  dangling_lot_link = gncScrubLotIsSingleLotLinkSplit (lot);
488  if (dangling_lot_link)
489  {
490  dangling_payments = gncScrubLotDanglingPayments (lot);
491  if (dangling_payments)
492  splits_deleted |= gncScrubLotLinks (lot);
493  else
494  {
495  Split *split = gnc_lot_get_earliest_split (lot);
496  Transaction *trans = xaccSplitGetParent (split);
497  xaccTransDestroy (trans);
498  }
499  }
500 
501  // If lot is empty now, delete it
502  if (0 == gnc_lot_count_splits (lot))
503  {
504  PINFO("All splits were removed from lot, deleting");
505  gnc_lot_destroy (lot);
506  }
507 
508  if (acc)
510 
511  LEAVE ("(lot=%s, deleted=%d, dangling lot link=%d, dangling_payments=%d)",
512  lotname ? lotname : "(no lotname)", splits_deleted, dangling_lot_link,
513  dangling_payments);
514  g_free (lotname);
515 
516  return splits_deleted;
517 }
518 
519 gboolean
520 gncScrubBusinessSplit (Split *split)
521 {
522  Transaction *txn;
523  gboolean deleted_split = FALSE;
524 
525  if (!split) return FALSE;
526  ENTER ("(split=%p)", split);
527 
528  txn = xaccSplitGetParent (split);
529  if (txn)
530  {
531  gchar txntype = xaccTransGetTxnType (txn);
532  const gchar *read_only = xaccTransGetReadOnly (txn);
533  gboolean is_void = xaccTransGetVoidStatus (txn);
534  GNCLot *lot = xaccSplitGetLot (split);
535  GncInvoice *invoice = gncInvoiceGetInvoiceFromTxn (txn);
536  Transaction *posted_txn = gncInvoiceGetPostedTxn (invoice);
537 
538  /* Look for transactions as a result of double posting an invoice or bill
539  * Refer to https://bugs.gnucash.org/show_bug.cgi?id=754209
540  * to learn how this could have happened in the past.
541  * Characteristics of such transaction are:
542  * - read only
543  * - not voided (to ensure read only is set by the business functions)
544  * - transaction type is none (should be type invoice for proper post transactions)
545  * - assigned to a lot
546  */
547  if ((txntype == TXN_TYPE_NONE) && read_only && !is_void && lot)
548  {
549  const gchar *memo = _("Please delete this transaction. Explanation at https://wiki.gnucash.org/wiki/Business_Features_Issues#Double_posting");
550  gchar *txn_date = qof_print_date (xaccTransGetDateEntered (txn));
551  xaccTransClearReadOnly (txn);
552  xaccSplitSetMemo (split, memo);
553  gnc_lot_remove_split (lot, split);
554  PWARN("Cleared double post status of transaction \"%s\", dated %s. "
555  "Please delete transaction and verify balance.",
557  txn_date);
558  g_free (txn_date);
559  }
560  /* Next check for transactions which claim to be the posted transaction of
561  * an invoice but the invoice disagrees. In that case
562  */
563  else if (invoice && (txn != posted_txn))
564  {
565  const gchar *memo = _("Please delete this transaction. Explanation at https://wiki.gnucash.org/wiki/Business_Features_Issues#I_can.27t_delete_a_transaction_of_type_.22I.22_from_the_AR.2FAP_account");
566  gchar *txn_date = qof_print_date (xaccTransGetDateEntered (txn));
567  xaccTransClearReadOnly (txn);
569  xaccSplitSetMemo (split, memo);
570  if (lot)
571  {
572  gnc_lot_remove_split (lot, split);
573  gncInvoiceDetachFromLot (lot);
574  gncOwnerAttachToLot (gncInvoiceGetOwner(invoice), lot);
575  }
576  PWARN("Cleared double post status of transaction \"%s\", dated %s. "
577  "Please delete transaction and verify balance.",
579  txn_date);
580  g_free (txn_date);
581  }
582  /* Next delete any empty splits that aren't part of an invoice transaction
583  * Such splits may be the result of scrubbing the business lots, which can
584  * merge splits together while reducing superfluous lot links
585  */
586  else if (gnc_numeric_zero_p (xaccSplitGetAmount(split)) && !gncInvoiceGetInvoiceFromTxn (txn) && !is_void)
587  {
588  GNCLot *lot = xaccSplitGetLot (split);
589  time64 pdate = xaccTransGetDate (txn);
590  gchar *pdatestr = gnc_ctime (&pdate);
591  PINFO ("Destroying empty split %p from transaction %s (%s)", split, pdatestr, xaccTransGetDescription(txn));
592  xaccSplitDestroy (split);
593  g_free (pdatestr);
594 
595  // Also delete the lot containing this split if it was the last split in that lot
596  if (lot && (gnc_lot_count_splits (lot) == 0))
597  gnc_lot_destroy (lot);
598 
599  deleted_split = TRUE;
600  }
601 
602  }
603 
604  LEAVE ("(split=%p)", split);
605  return deleted_split;
606 }
607 
608 /* ============================================================== */
609 
610 void
612 {
613  LotList *lots, *node;
614  gint lot_count = 0;
615  gint curr_lot_no = 0;
616  const gchar *str;
617  const char *message = _( "Checking business lots in account %s: %u of %u");
618 
619  if (!acc) return;
620 
621  if (gnc_get_abort_scrub())
622  (percentagefunc)(NULL, -1.0);
623 
624  if (FALSE == xaccAccountIsAPARType (xaccAccountGetType (acc))) return;
625 
626  str = xaccAccountGetName(acc);
627  str = str ? str : "(null)";
628 
629  ENTER ("(acc=%s)", str);
630  PINFO ("Cleaning up superfluous lot links in account %s\n", str);
632 
633  lots = xaccAccountGetLotList(acc);
634  lot_count = g_list_length (lots);
635  for (node = lots; node; node = node->next)
636  {
637  GNCLot *lot = node->data;
638 
639  PINFO("Start processing lot %d of %d",
640  curr_lot_no + 1, lot_count);
641 
642  if (curr_lot_no % 100 == 0)
643  {
644  char *progress_msg = g_strdup_printf (message, str, curr_lot_no, lot_count);
645  (percentagefunc)(progress_msg, (100 * curr_lot_no) / lot_count);
646  g_free (progress_msg);
647  }
648 
649  if (lot)
650  gncScrubBusinessLot (lot);
651 
652  PINFO("Finished processing lot %d of %d",
653  curr_lot_no + 1, lot_count);
654  curr_lot_no++;
655  }
656  g_list_free(lots);
658  (percentagefunc)(NULL, -1.0);
659  LEAVE ("(acc=%s)", str);
660 }
661 
662 /* ============================================================== */
663 
664 void
666 {
667  SplitList *splits, *node;
668  gint split_count = 0;
669  gint curr_split_no;
670  const gchar *str;
671  const char *message = _( "Checking business splits in account %s: %u of %u");
672 
673  if (!acc) return;
674 
675  if (gnc_get_abort_scrub())
676  (percentagefunc)(NULL, -1.0);
677 
678  if (FALSE == xaccAccountIsAPARType (xaccAccountGetType (acc))) return;
679 
680  str = xaccAccountGetName(acc);
681  str = str ? str : "(null)";
682 
683  ENTER ("(acc=%s)", str);
684  PINFO ("Cleaning up superfluous lot links in account %s\n", str);
686 
687 restart:
688  curr_split_no = 0;
689  splits = xaccAccountGetSplitList(acc);
690  split_count = g_list_length (splits);
691  for (node = splits; node; node = node->next)
692  {
693  Split *split = node->data;
694 
695  PINFO("Start processing split %d of %d",
696  curr_split_no + 1, split_count);
697 
698  if (gnc_get_abort_scrub ())
699  break;
700 
701  if (curr_split_no % 100 == 0)
702  {
703  char *progress_msg = g_strdup_printf (message, str, curr_split_no, split_count);
704  (percentagefunc)(progress_msg, (100 * curr_split_no) / split_count);
705  g_free (progress_msg);
706  }
707 
708  if (split)
709  // If gncScrubBusinessSplit returns true, a split was deleted and hence
710  // The account split list has become invalid, so we need to start over
711  if (gncScrubBusinessSplit (split))
712  goto restart;
713 
714  PINFO("Finished processing split %d of %d",
715  curr_split_no + 1, split_count);
716  curr_split_no++;
717  }
719  (percentagefunc)(NULL, -1.0);
720  LEAVE ("(acc=%s)", str);
721 }
722 
723 /* ============================================================== */
724 
725 void
727 {
728  if (!acc) return;
729  if (FALSE == xaccAccountIsAPARType (xaccAccountGetType (acc))) return;
730 
731  gncScrubBusinessAccountLots (acc, percentagefunc);
732  gncScrubBusinessAccountSplits (acc, percentagefunc);
733 }
734 
735 /* ============================================================== */
736 
737 static void
738 lot_scrub_cb (Account *acc, gpointer data)
739 {
740  if (FALSE == xaccAccountIsAPARType (xaccAccountGetType (acc))) return;
741  gncScrubBusinessAccount (acc, data);
742 }
743 
744 void
746 {
747  if (!acc) return;
748 
749  gnc_account_foreach_descendant(acc, lot_scrub_cb, percentagefunc);
750  gncScrubBusinessAccount (acc, percentagefunc);
751 }
752 
753 /* ========================== END OF FILE ========================= */
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
char xaccTransGetTxnType(const Transaction *trans)
Returns the Transaction Type.
Definition: Transaction.c:2548
GList LotList
GList of GNCLots.
Definition: gnc-engine.h:209
void(* QofPercentageFunc)(const char *message, double percent)
The qof_session_load() method causes the QofBook to be made ready to to use with this URL/datastore...
Definition: qofsession.h:199
time64 xaccTransGetDate(const Transaction *trans)
Retrieve the posted date of the transaction.
SplitList * xaccAccountGetSplitList(const Account *acc)
The xaccAccountGetSplitList() routine returns a pointer to a GList of the splits in the account...
Definition: Account.cpp:3964
void gnc_account_foreach_descendant(const Account *acc, AccountCb thunk, gpointer user_data)
This method will traverse all children of this accounts and their descendants, calling &#39;func&#39; on each...
Definition: Account.cpp:3202
#define G_LOG_DOMAIN
Functions providing the SX List as a plugin page.
#define TXN_TYPE_INVOICE
Transaction is an invoice.
Definition: Transaction.h:125
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
GNCAccountType xaccAccountGetType(const Account *acc)
Returns the account&#39;s account type.
Definition: Account.cpp:3236
gboolean xaccSplitDestroy(Split *split)
Destructor.
Definition: Split.c:1470
Utilities to Convert Stock Accounts to use Lots.
gnc_numeric gnc_numeric_neg(gnc_numeric a)
Returns a newly created gnc_numeric that is the negative of the given gnc_numeric value...
void gncScrubBusinessAccountSplits(Account *acc, QofPercentageFunc percentagefunc)
The gncScrubBusinessAccountSplits() function will call gncScrubBusinessSplit() on each split in the g...
const char * xaccTransGetReadOnly(Transaction *trans)
Returns a non-NULL value if this Transaction was marked as read-only with some specific "reason" text...
Definition: Transaction.c:2567
void gnc_lot_add_split(GNCLot *lot, Split *split)
The gnc_lot_add_split() routine adds a split to this lot.
Definition: gnc-lot.c:622
gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Return a+b.
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
Transaction * xaccSplitGetParent(const Split *split)
Returns the parent transaction of the split.
int gnc_numeric_compare(gnc_numeric a, gnc_numeric b)
Returns 1 if a>b, -1 if b>a, 0 if a == b.
gchar * gnc_numeric_to_string(gnc_numeric n)
Convert to string.
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
Cleanup functions for business objects.
Split * gnc_lot_get_earliest_split(GNCLot *lot)
The gnc_lot_get_earliest_split() routine is a convenience routine that helps identify the earliest da...
Definition: gnc-lot.c:701
void gncScrubBusinessAccountLots(Account *acc, QofPercentageFunc percentagefunc)
The gncScrubBusinessAccountLots() function will call gncScrubBusinessLot() on each lot in the given a...
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
void xaccTransSetTxnType(Transaction *trans, char type)
Set the Transaction Type.
Definition: Transaction.c:2109
#define TXN_TYPE_NONE
No transaction type.
Definition: Transaction.h:124
convert single-entry accounts to clean double-entry
char * qof_print_date(time64 secs)
Convenience; calls through to qof_print_date_dmy_buff().
Definition: gnc-date.cpp:617
GList SplitList
GList of Split.
Definition: gnc-engine.h:211
Account handling public routines.
void xaccSplitSetMemo(Split *split, const char *memo)
The memo is an arbitrary string associated with a split.
Definition: Split.c:1728
gboolean gncOwnerReduceSplitTo(Split *split, gnc_numeric target_value)
Helper function to reduce the value of a split to target_value.
Definition: gncOwner.c:979
Implement Accounting Policy Private header File.
gboolean gncScrubBusinessSplit(Split *split)
The gncScrubBusinessSplit() function will fix all issues found with the given split.
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
LotList * xaccAccountGetLotList(const Account *acc)
The xaccAccountGetLotList() routine returns a list of all lots in this account.
Definition: Account.cpp:4008
void gncScrubBusinessAccount(Account *acc, QofPercentageFunc percentagefunc)
The gncScrubBusinessAccount() function will call all scrub functions relevant for a given account on ...
const char * xaccTransGetDescription(const Transaction *trans)
Gets the transaction Description.
void gncOwnerAttachToLot(const GncOwner *owner, GNCLot *lot)
Attach an owner to a lot.
Definition: gncOwner.c:623
gboolean xaccAccountIsAPARType(GNCAccountType t)
Convenience function to check if the account is a valid business account type (meaning an Accounts Pa...
Definition: Account.cpp:4634
gnc_numeric gnc_numeric_abs(gnc_numeric a)
Returns a newly created gnc_numeric that is the absolute value of the given gnc_numeric value...
Find the least common multiple of the arguments&#39; denominators and use that as the denominator of the ...
Definition: gnc-numeric.h:201
void xaccTransCommitEdit(Transaction *trans)
The xaccTransCommitEdit() method indicates that the changes to the transaction and its splits are com...
#define TXN_TYPE_LINK
Transaction is a link between (invoice and payment) lots.
Definition: Transaction.h:127
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.
gboolean gnc_numeric_positive_p(gnc_numeric a)
Returns 1 if a > 0, otherwise returns 0.
time64 xaccTransGetDateEntered(const Transaction *trans)
Retrieve the date of when the transaction was entered.
Definition: Transaction.c:2477
GncInvoice * gncInvoiceGetInvoiceFromLot(GNCLot *lot)
Given a LOT, find and return the Invoice attached to the lot.
Definition: gncInvoice.c:1301
Business Invoice Interface.
gboolean xaccTransGetVoidStatus(const Transaction *trans)
Retrieve information on whether or not a transaction has been voided.
Definition: Transaction.c:2799
gnc_numeric xaccSplitGetValue(const Split *split)
Returns the value of this split in the transaction&#39;s commodity.
Definition: gmock-Split.cpp:84
gboolean gncScrubBusinessLot(GNCLot *lot)
The gncScrubBusinessLot() function makes sure that the indicated lot has all the correct properties r...
void xaccAccountBeginEdit(Account *acc)
The xaccAccountBeginEdit() subroutine is the first phase of a two-phase-commit wrapper for account up...
Definition: Account.cpp:1430
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
Split * gncOwnerFindOffsettingSplit(GNCLot *lot, gnc_numeric target_value)
Helper function to find a split in lot that best offsets target_value Obviously it should be of oppos...
Definition: gncOwner.c:913
void gncOwnerSetLotLinkMemo(Transaction *ll_txn)
To help a user understand what a lot link transaction does, we set the memo to name all documents inv...
Definition: gncOwner.c:1013
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
void gncScrubBusinessAccountTree(Account *acc, QofPercentageFunc percentagefunc)
The gncScrubBusinessAccountTreeLots() function will call gncScrubBusinessAccount() on the given accou...
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
SplitList * xaccTransGetSplitList(const Transaction *trans)
The xaccTransGetSplitList() method returns a GList of the splits in a transaction.
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
gnc_numeric xaccSplitGetAmount(const Split *split)
Returns the amount of the split in the account&#39;s commodity.
Definition: gmock-Split.cpp:69