GnuCash  5.6-150-g038405b370+
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 immediately removed from the list.
218  if (gnc_numeric_zero_p (xaccSplitGetValue (sl_split)))
219  {
220  GList *tmp_iter = sls_iter->prev;
221  PINFO("Removing 0-value split from the lot.");
222 
224  gnc_lot_remove_split (scrub_lot, sl_split);
225  else
226  xaccSplitDestroy (sl_split);
227 
228  sls_iter = tmp_iter;
229  if (!sls_iter)
230  goto scrub_start; // Otherwise sls_iter->next will crash
231  continue;
232  }
233 
234  // Iterate over all splits in the lot link transaction
235  for (lts_iter = xaccTransGetSplitList (ll_txn); lts_iter; lts_iter = lts_iter->next)
236  {
237  Split *ll_txn_split = lts_iter->data; // These all refer to splits in the lot link transaction
238  GNCLot *remote_lot = NULL; // lot at the other end of the lot link transaction
239  gboolean sl_is_doc_lot, rl_is_doc_lot;
240 
241  if (!ll_txn_split)
242  continue; // next lot link transaction split
243 
244  // Skip the split in the lot we're currently scrubbing
245  if (sl_split == ll_txn_split)
246  continue; // next lot link transaction split
247 
248  // Skip empty other splits. They'll be scrubbed in the outer for loop later
249  if (gnc_numeric_zero_p (xaccSplitGetValue (ll_txn_split)) ||
250  gnc_numeric_zero_p(xaccSplitGetValue (ll_txn_split)))
251  continue;
252 
253  // Only splits of opposite signed values can be scrubbed
254  if (gnc_numeric_positive_p (xaccSplitGetValue (sl_split)) ==
255  gnc_numeric_positive_p (xaccSplitGetValue (ll_txn_split)))
256  continue; // next lot link transaction split
257 
258  // We can only scrub if the other split is in a lot as well
259  // Link transactions always have their other split in another lot
260  // however ordinary payment transactions may not
261  remote_lot = xaccSplitGetLot (ll_txn_split);
262  if (!remote_lot)
263  continue;
264 
265  sl_is_doc_lot = (gncInvoiceGetInvoiceFromLot (scrub_lot) != NULL);
266  rl_is_doc_lot = (gncInvoiceGetInvoiceFromLot (remote_lot) != NULL);
267 
268  // Depending on the type of lots we're comparing, we need different actions
269  // - Two document lots (an invoice and a credit note):
270  // Special treatment - look for all document lots linked via ll_txn
271  // and update the memo to be of more use to the users.
272  // - Two payment lots:
273  // (Part of) the link will be eliminated and instead (part of)
274  // one payment will be added to the other lot to keep the balance.
275  // If the payments are not equal in abs value part of the bigger payment
276  // will be moved to the smaller payment's lot.
277  // - A document and a payment lot:
278  // (Part of) the link will be eliminated and instead (part of) the real
279  // payment will be added to the document lot to handle the payment.
280  if (sl_is_doc_lot && rl_is_doc_lot)
281  gncOwnerSetLotLinkMemo (ll_txn);
282  else if (!sl_is_doc_lot && !rl_is_doc_lot)
283  {
284  gint cmp = gnc_numeric_compare (gnc_numeric_abs (xaccSplitGetValue (sl_split)),
285  gnc_numeric_abs (xaccSplitGetValue (ll_txn_split)));
286  if (cmp >= 0)
287  restart_needed = scrub_other_link (scrub_lot, sl_split, remote_lot, ll_txn_split);
288  else
289  restart_needed = scrub_other_link (remote_lot, ll_txn_split, scrub_lot, sl_split);
290  }
291  else
292  {
293  GNCLot *doc_lot = sl_is_doc_lot ? scrub_lot : remote_lot;
294  GNCLot *pay_lot = sl_is_doc_lot ? remote_lot : scrub_lot;
295  Split *ll_doc_split = sl_is_doc_lot ? sl_split : ll_txn_split;
296  Split *ll_pay_split = sl_is_doc_lot ? ll_txn_split : sl_split;
297  // Ok, let's try to move a payment from pay_lot to doc_lot
298  restart_needed = scrub_other_link (pay_lot, ll_pay_split, doc_lot, ll_doc_split);
299  }
300 
301  // If we got here, the splits in our lot and ll_txn have been severely mixed up
302  // And our iterator lists are probably no longer valid
303  // So let's start over
304  if (restart_needed)
305  {
306  modified = TRUE;
307  goto scrub_start;
308  }
309 
310  }
311  }
312 
313  return modified;
314 }
315 
316 // Note this is a recursive function. It presumes the number of splits
317 // in avail_splits is relatively low. With many splits the performance will
318 // quickly degrade.
319 // Careful: this function assumes all splits in avail_splits to be valid
320 // and with values of opposite sign of target_value
321 // Ignoring this can cause unexpected results!
322 static SplitList *
323 gncSLFindOffsSplits (SplitList *avail_splits, gnc_numeric target_value)
324 {
325  gint curr_recurse_level = 0;
326  gint max_recurse_level = g_list_length (avail_splits) - 1;
327 
328  if (!avail_splits)
329  return NULL;
330 
331  for (curr_recurse_level = 0;
332  curr_recurse_level <= max_recurse_level;
333  curr_recurse_level++)
334  {
335  SplitList *split_iter = NULL;
336  for (split_iter = avail_splits; split_iter; split_iter = split_iter->next)
337  {
338  Split *split = split_iter->data;
339  SplitList *match_splits = NULL;
340  gnc_numeric split_value, remaining_value;
341 
342  split_value = xaccSplitGetValue (split);
343  // Attention: target_value and split_value are of opposite sign
344  // So to get the remaining target value, they should be *added*
345  remaining_value = gnc_numeric_add (target_value, split_value,
347 
348  if (curr_recurse_level == 0)
349  {
350  if (gnc_numeric_zero_p (remaining_value))
351  match_splits = g_list_prepend (NULL, split);
352  }
353  else
354  {
355  if (gnc_numeric_positive_p (target_value) ==
356  gnc_numeric_positive_p (remaining_value))
357  match_splits = gncSLFindOffsSplits (split_iter->next,
358  remaining_value);
359  }
360 
361  if (match_splits)
362  return g_list_prepend (match_splits, split);
363  }
364  }
365 
366  return NULL;
367 }
368 
369 
370 static gboolean
371 gncScrubLotDanglingPayments (GNCLot *lot)
372 {
373  SplitList * split_list, *filtered_list = NULL, *match_list = NULL, *node;
374  Split *ll_split = gnc_lot_get_earliest_split (lot);
375  Transaction *ll_trans = xaccSplitGetParent (ll_split);
376  gnc_numeric ll_val = xaccSplitGetValue (ll_split);
377  time64 ll_date = xaccTransGetDate (ll_trans);
378  const char *ll_desc = xaccTransGetDescription (ll_trans);
379 
380  // look for free splits (i.e. not in any lot) which,
381  // compared to the lot link split
382  // - have the same date
383  // - have the same description
384  // - have an opposite sign amount
385  // - free split's abs value is less than or equal to ll split's abs value
386  split_list = xaccAccountGetSplitList(gnc_lot_get_account (lot));
387  for (node = split_list; node; node = node->next)
388  {
389  Split *free_split = node->data;
390  Transaction *free_trans;
391  gnc_numeric free_val;
392 
393  if (NULL != xaccSplitGetLot(free_split))
394  continue;
395 
396  free_trans = xaccSplitGetParent (free_split);
397  if (ll_date != xaccTransGetDate (free_trans))
398  continue;
399 
400  if (0 != g_strcmp0 (ll_desc, xaccTransGetDescription (free_trans)))
401  continue;
402 
403  free_val = xaccSplitGetValue (free_split);
404  if (gnc_numeric_positive_p (ll_val) ==
405  gnc_numeric_positive_p (free_val))
406  continue;
407 
408  if (gnc_numeric_compare (gnc_numeric_abs (free_val), gnc_numeric_abs (ll_val)) > 0)
409  continue;
410 
411  filtered_list = g_list_prepend (filtered_list, free_split);
412  }
413  g_list_free (split_list);
414 
415  filtered_list = g_list_reverse (filtered_list);
416  match_list = gncSLFindOffsSplits (filtered_list, ll_val);
417  g_list_free (filtered_list);
418 
419  for (node = match_list; node; node = node->next)
420  {
421  Split *match_split = node->data;
422  gnc_lot_add_split (lot, match_split);
423  }
424 
425  if (match_list)
426  {
427  g_list_free (match_list);
428  return TRUE;
429  }
430  else
431  return FALSE;
432 }
433 
434 static gboolean
435 gncScrubLotIsSingleLotLinkSplit (GNCLot *lot)
436 {
437  Split *split = NULL;
438  Transaction *trans = NULL;
439 
440  // Lots with a single split which is also a lot link transaction split
441  // may be sign of a dangling payment. Let's try to fix that
442 
443  // Only works for single split lots...
444  if (1 != gnc_lot_count_splits (lot))
445  return FALSE;
446 
447  split = gnc_lot_get_earliest_split (lot);
448  trans = xaccSplitGetParent (split);
449 
450  if (!trans)
451  {
452  // Ooops - the split doesn't belong to any transaction !
453  // This is not expected so issue a warning and continue with next split
454  PWARN("Encountered a split in a business lot that's not part of any transaction. "
455  "This is unexpected! Skipping split %p.", split);
456  return FALSE;
457  }
458 
459  // Only works if single split belongs to a lot link transaction...
460  if (xaccTransGetTxnType (trans) != TXN_TYPE_LINK)
461  return FALSE;
462 
463  return TRUE;
464 }
465 
466 gboolean
467 gncScrubBusinessLot (GNCLot *lot)
468 {
469  gboolean splits_deleted = FALSE;
470  gboolean dangling_payments = FALSE;
471  gboolean dangling_lot_link = FALSE;
472  Account *acc;
473  gchar *lotname=NULL;
474 
475  if (!lot) return FALSE;
476  lotname = g_strdup (gnc_lot_get_title (lot));
477  ENTER ("(lot=%p) %s", lot, lotname ? lotname : "(no lotname)");
478 
479  acc = gnc_lot_get_account (lot);
480  if (acc)
482 
483  /* Check invoice link consistency
484  * A lot should have both or neither of:
485  * - one split from an invoice transaction
486  * - an invoice-guid set
487  */
488  gncScrubInvoiceState (lot);
489 
490  // Scrub lot links.
491  // They should only remain when two document lots are linked together
492  xaccScrubMergeLotSubSplits (lot, FALSE);
493  splits_deleted = gncScrubLotLinks (lot);
494 
495  // Look for dangling payments and repair if found
496  dangling_lot_link = gncScrubLotIsSingleLotLinkSplit (lot);
497  if (dangling_lot_link)
498  {
499  dangling_payments = gncScrubLotDanglingPayments (lot);
500  if (dangling_payments)
501  splits_deleted |= gncScrubLotLinks (lot);
502  else
503  {
504  Split *split = gnc_lot_get_earliest_split (lot);
505  Transaction *trans = xaccSplitGetParent (split);
506  xaccTransDestroy (trans);
507  }
508  }
509 
510  // If lot is empty now, delete it
511  if (0 == gnc_lot_count_splits (lot))
512  {
513  PINFO("All splits were removed from lot, deleting");
514  gnc_lot_destroy (lot);
515  }
516 
517  if (acc)
519 
520  LEAVE ("(lot=%s, deleted=%d, dangling lot link=%d, dangling_payments=%d)",
521  lotname ? lotname : "(no lotname)", splits_deleted, dangling_lot_link,
522  dangling_payments);
523  g_free (lotname);
524 
525  return splits_deleted;
526 }
527 
528 gboolean
529 gncScrubBusinessSplit (Split *split)
530 {
531  Transaction *txn;
532  gboolean deleted_split = FALSE;
533 
534  if (!split) return FALSE;
535  ENTER ("(split=%p)", split);
536 
537  txn = xaccSplitGetParent (split);
538  if (txn)
539  {
540  gchar txntype = xaccTransGetTxnType (txn);
541  const gchar *read_only = xaccTransGetReadOnly (txn);
542  gboolean is_void = xaccTransGetVoidStatus (txn);
543  GNCLot *lot = xaccSplitGetLot (split);
544  GncInvoice *invoice = gncInvoiceGetInvoiceFromTxn (txn);
545  Transaction *posted_txn = gncInvoiceGetPostedTxn (invoice);
546 
547  /* Look for transactions as a result of double posting an invoice or bill
548  * Refer to https://bugs.gnucash.org/show_bug.cgi?id=754209
549  * to learn how this could have happened in the past.
550  * Characteristics of such transaction are:
551  * - read only
552  * - not voided (to ensure read only is set by the business functions)
553  * - transaction type is none (should be type invoice for proper post transactions)
554  * - assigned to a lot
555  */
556  if ((txntype == TXN_TYPE_NONE) && read_only && !is_void && lot)
557  {
558  const gchar *memo = _("Please delete this transaction. Explanation at https://wiki.gnucash.org/wiki/Business_Features_Issues#Double_posting");
559  gchar *txn_date = qof_print_date (xaccTransGetDateEntered (txn));
560  xaccTransClearReadOnly (txn);
561  xaccSplitSetMemo (split, memo);
562  gnc_lot_remove_split (lot, split);
563  PWARN("Cleared double post status of transaction \"%s\", dated %s. "
564  "Please delete transaction and verify balance.",
566  txn_date);
567  g_free (txn_date);
568  }
569  /* Next check for transactions which claim to be the posted transaction of
570  * an invoice but the invoice disagrees. In that case
571  */
572  else if (invoice && (txn != posted_txn))
573  {
574  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");
575  gchar *txn_date = qof_print_date (xaccTransGetDateEntered (txn));
576  xaccTransClearReadOnly (txn);
578  xaccSplitSetMemo (split, memo);
579  if (lot)
580  {
581  gnc_lot_remove_split (lot, split);
582  gncInvoiceDetachFromLot (lot);
583  gncOwnerAttachToLot (gncInvoiceGetOwner(invoice), lot);
584  }
585  PWARN("Cleared double post status of transaction \"%s\", dated %s. "
586  "Please delete transaction and verify balance.",
588  txn_date);
589  g_free (txn_date);
590  }
591  /* Next delete any empty splits that aren't part of an invoice transaction
592  * Such splits may be the result of scrubbing the business lots, which can
593  * merge splits together while reducing superfluous lot links
594  */
595  else if (gnc_numeric_zero_p (xaccSplitGetAmount(split)) && !gncInvoiceGetInvoiceFromTxn (txn) && !is_void)
596  {
597  GNCLot *lot = xaccSplitGetLot (split);
598  time64 pdate = xaccTransGetDate (txn);
599  gchar *pdatestr = gnc_ctime (&pdate);
600  PINFO ("Destroying empty split %p from transaction %s (%s)", split, pdatestr, xaccTransGetDescription(txn));
601  xaccSplitDestroy (split);
602  g_free (pdatestr);
603 
604  // Also delete the lot containing this split if it was the last split in that lot
605  if (lot && (gnc_lot_count_splits (lot) == 0))
606  gnc_lot_destroy (lot);
607 
608  deleted_split = TRUE;
609  }
610 
611  }
612 
613  LEAVE ("(split=%p)", split);
614  return deleted_split;
615 }
616 
617 /* ============================================================== */
618 
619 void
621 {
622  LotList *lots, *node;
623  gint lot_count = 0;
624  gint curr_lot_no = 0;
625  const gchar *str;
626  const char *message = _( "Checking business lots in account %s: %u of %u");
627 
628  if (!acc) return;
629 
630  if (gnc_get_abort_scrub())
631  (percentagefunc)(NULL, -1.0);
632 
633  if (FALSE == xaccAccountIsAPARType (xaccAccountGetType (acc))) return;
634 
635  str = xaccAccountGetName(acc);
636  str = str ? str : "(null)";
637 
638  ENTER ("(acc=%s)", str);
639  PINFO ("Cleaning up superfluous lot links in account %s\n", str);
641 
642  lots = xaccAccountGetLotList(acc);
643  lot_count = g_list_length (lots);
644  for (node = lots; node; node = node->next)
645  {
646  GNCLot *lot = node->data;
647 
648  PINFO("Start processing lot %d of %d",
649  curr_lot_no + 1, lot_count);
650 
651  if (curr_lot_no % 100 == 0)
652  {
653  char *progress_msg = g_strdup_printf (message, str, curr_lot_no, lot_count);
654  (percentagefunc)(progress_msg, (100 * curr_lot_no) / lot_count);
655  g_free (progress_msg);
656  }
657 
658  if (lot)
659  gncScrubBusinessLot (lot);
660 
661  PINFO("Finished processing lot %d of %d",
662  curr_lot_no + 1, lot_count);
663  curr_lot_no++;
664  }
665  g_list_free(lots);
667  (percentagefunc)(NULL, -1.0);
668  LEAVE ("(acc=%s)", str);
669 }
670 
671 /* ============================================================== */
672 
673 void
675 {
676  SplitList *splits, *node;
677  gint split_count = 0;
678  gint curr_split_no;
679  const gchar *str;
680  const char *message = _( "Checking business splits in account %s: %u of %u");
681 
682  if (!acc) return;
683 
684  if (gnc_get_abort_scrub())
685  (percentagefunc)(NULL, -1.0);
686 
687  if (FALSE == xaccAccountIsAPARType (xaccAccountGetType (acc))) return;
688 
689  str = xaccAccountGetName(acc);
690  str = str ? str : "(null)";
691 
692  ENTER ("(acc=%s)", str);
693  PINFO ("Cleaning up superfluous lot links in account %s\n", str);
695 
696 restart:
697  curr_split_no = 0;
698  splits = xaccAccountGetSplitList(acc);
699  split_count = xaccAccountGetSplitsSize (acc);
700  for (node = splits; node; node = node->next)
701  {
702  Split *split = node->data;
703 
704  PINFO("Start processing split %d of %d",
705  curr_split_no + 1, split_count);
706 
707  if (gnc_get_abort_scrub ())
708  break;
709 
710  if (curr_split_no % 100 == 0)
711  {
712  char *progress_msg = g_strdup_printf (message, str, curr_split_no, split_count);
713  (percentagefunc)(progress_msg, (100 * curr_split_no) / split_count);
714  g_free (progress_msg);
715  }
716 
717  if (split)
718  // If gncScrubBusinessSplit returns true, a split was deleted and hence
719  // The account split list has become invalid, so we need to start over
720  if (gncScrubBusinessSplit (split))
721  goto restart;
722 
723  PINFO("Finished processing split %d of %d",
724  curr_split_no + 1, split_count);
725  curr_split_no++;
726  }
727  g_list_free (splits);
729  (percentagefunc)(NULL, -1.0);
730  LEAVE ("(acc=%s)", str);
731 }
732 
733 /* ============================================================== */
734 
735 void
737 {
738  if (!acc) return;
739  if (FALSE == xaccAccountIsAPARType (xaccAccountGetType (acc))) return;
740 
741  gncScrubBusinessAccountLots (acc, percentagefunc);
742  gncScrubBusinessAccountSplits (acc, percentagefunc);
743 }
744 
745 /* ============================================================== */
746 
747 static void
748 lot_scrub_cb (Account *acc, gpointer data)
749 {
750  if (FALSE == xaccAccountIsAPARType (xaccAccountGetType (acc))) return;
751  gncScrubBusinessAccount (acc, data);
752 }
753 
754 void
756 {
757  if (!acc) return;
758 
759  gnc_account_foreach_descendant(acc, lot_scrub_cb, percentagefunc);
760  gncScrubBusinessAccount (acc, percentagefunc);
761 }
762 
763 /* ========================== 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.cpp:379
GList LotList
GList of GNCLots.
Definition: gnc-engine.h:205
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:3967
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:3197
char xaccTransGetTxnType(Transaction *trans)
Returns the Transaction Type: note this type will be derived from the transaction splits...
#define G_LOG_DOMAIN
Functions providing the SX List as a plugin page.
#define TXN_TYPE_INVOICE
Transaction is an invoice.
Definition: Transaction.h:126
#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:3228
gboolean xaccSplitDestroy(Split *split)
Destructor.
Definition: Split.cpp:1471
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...
STRUCTS.
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...
void gnc_lot_add_split(GNCLot *lot, Split *split)
Adds a split to this lot.
Definition: gnc-lot.cpp:594
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)
Convenience routine to identify the earliest date in the lot.
Definition: gnc-lot.cpp:673
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:1327
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: note the type will be saved into the Transaction kvp property as a backward...
#define TXN_TYPE_NONE
No transaction type.
Definition: Transaction.h:125
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:608
GList SplitList
GList of Split.
Definition: gnc-engine.h:207
Account handling public routines.
Find the least common multiple of the arguments&#39; denominators and use that as the denominator of the ...
Definition: gnc-numeric.h:200
void xaccSplitSetMemo(Split *split, const char *memo)
The memo is an arbitrary string associated with a split.
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)
Returns a list of all the splits in this lot.
Definition: gnc-lot.cpp:425
LotList * xaccAccountGetLotList(const Account *acc)
The xaccAccountGetLotList() routine returns a list of all lots in this account.
Definition: Account.cpp:3991
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:622
void gnc_lot_remove_split(GNCLot *lot, Split *split)
Adds a split from this lot.
Definition: gnc-lot.cpp:646
gboolean xaccAccountIsAPARType(GNCAccountType t)
Convenience function to check if the account is a valid business account type (meaning an Accounts Pa...
Definition: Account.cpp:4616
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...
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:128
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.
GncInvoice * gncInvoiceGetInvoiceFromLot(GNCLot *lot)
Given a LOT, find and return the Invoice attached to the lot.
Definition: gncInvoice.c:1289
Business Invoice Interface.
gboolean xaccTransGetVoidStatus(const Transaction *trans)
Retrieve information on whether or not a transaction has been voided.
Split * gncOwnerFindOffsettingSplit(GNCLot *lot, gnc_numeric target_amount)
Helper function to find a split in lot that best offsets target_amount Obviously it should be of oppo...
Definition: gncOwner.c:896
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:1479
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
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:1010
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
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:255
const char * xaccAccountGetName(const Account *acc)
Get the account&#39;s name.
Definition: Account.cpp:3250
#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
gboolean gncOwnerReduceSplitTo(Split *split, gnc_numeric target_amount)
Helper function to reduce the amount of a split to target_amount.
Definition: gncOwner.c:959
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.cpp:1882
gnc_numeric xaccSplitGetAmount(const Split *split)
Returns the amount of the split in the account&#39;s commodity.
Definition: gmock-Split.cpp:69