GnuCash c935c2f+
Loading...
Searching...
No Matches
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
49static QofLogModule log_module = G_LOG_DOMAIN;
50
51static void
52gncScrubInvoiceState (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
94static 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
119static gboolean
120scrub_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
183static gboolean
184gncScrubLotLinks (GNCLot *scrub_lot)
185{
186 gboolean modified = FALSE, restart_needed = FALSE;
187 SplitList *sls_iter = NULL;
188
189scrub_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
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)) ||
251 continue;
252
253 // Only splits of opposite signed values can be scrubbed
254 if (gnc_numeric_positive_p (xaccSplitGetValue (sl_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 {
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!
322static SplitList *
323gncSLFindOffsSplits (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,
346 GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
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
370static gboolean
371gncScrubLotDanglingPayments (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
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
434static gboolean
435gncScrubLotIsSingleLotLinkSplit (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
466gboolean
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
528gboolean
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
619void
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)
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
673void
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
696restart:
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
735void
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
747static void
748lot_scrub_cb (Account *acc, gpointer data)
749{
750 if (FALSE == xaccAccountIsAPARType (xaccAccountGetType (acc))) return;
751 gncScrubBusinessAccount (acc, data);
752}
753
754void
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 ========================= */
Account handling public routines.
Utilities to Convert Stock Accounts to use Lots.
Cleanup functions for business objects.
convert single-entry accounts to clean double-entry
API for Transactions and Splits (journal entries)
All type declarations for the whole Gnucash engine.
Business Invoice Interface.
const char * xaccAccountGetName(const Account *acc)
Get the account's name.
Definition Account.cpp:3289
void xaccAccountCommitEdit(Account *acc)
ThexaccAccountCommitEdit() subroutine is the second phase of a two-phase-commit wrapper for account u...
Definition Account.cpp:1516
void xaccAccountBeginEdit(Account *acc)
The xaccAccountBeginEdit() subroutine is the first phase of a two-phase-commit wrapper for account up...
Definition Account.cpp:1475
GNCAccountType xaccAccountGetType(const Account *acc)
Returns the account's account type.
Definition Account.cpp:3267
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
char * gnc_ctime(const time64 *secs)
Return a string representation of a date from a 64-bit time value.
Definition gnc-date.cpp:256
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition gnc-date.h:87
char * qof_print_date(time64 t)
Convenience; calls through to qof_print_date_dmy_buff().
Definition gnc-date.cpp:610
LotList * xaccAccountGetLotList(const Account *acc)
The xaccAccountGetLotList() routine returns a list of all lots in this account.
Definition Account.cpp:3975
GList LotList
GList of GNCLots.
Definition gnc-engine.h:205
gboolean xaccTransGetVoidStatus(const Transaction *trans)
Retrieve information on whether or not a transaction has been voided.
char xaccTransGetTxnType(Transaction *trans)
Returns the Transaction Type: note this type will be derived from the transaction splits,...
void xaccTransDestroy(Transaction *trans)
Destroys a transaction.
void xaccTransSetTxnType(Transaction *trans, char type)
Set the Transaction Type: note the type will be saved into the Transaction kvp property as a backward...
void xaccTransCommitEdit(Transaction *trans)
The xaccTransCommitEdit() method indicates that the changes to the transaction and its splits are com...
const char * xaccTransGetDescription(const Transaction *trans)
Gets the transaction Description.
gboolean xaccAccountIsAPARType(GNCAccountType t)
Convenience function to check if the account is a valid business account type (meaning an Accounts Pa...
Definition Account.cpp:4527
#define TXN_TYPE_NONE
No transaction type
#define TXN_TYPE_INVOICE
Transaction is an invoice.
time64 xaccTransGetDateEntered(const Transaction *trans)
Retrieve the date of when the transaction was entered.
SplitList * xaccTransGetSplitList(const Transaction *trans)
The xaccTransGetSplitList() method returns a GList of the splits in a transaction.
GList SplitList
GList of Split.
Definition gnc-engine.h:207
void xaccTransBeginEdit(Transaction *trans)
The xaccTransBeginEdit() method must be called before any changes are made to a transaction or any of...
const char * xaccTransGetReadOnly(Transaction *trans)
Returns a non-NULL value if this Transaction was marked as read-only with some specific "reason" text...
SplitList * xaccAccountGetSplitList(const Account *acc)
The xaccAccountGetSplitList() routine returns a pointer to a GList of the splits in the account.
Definition Account.cpp:3949
#define TXN_TYPE_LINK
Transaction is a link between (invoice and payment) lots
time64 xaccTransGetDate(const Transaction *trans)
Retrieve the posted date of the transaction.
GncInvoice * gncInvoiceGetInvoiceFromTxn(const Transaction *txn)
Given a transaction, find and return the Invoice.
GncInvoice * gncInvoiceGetInvoiceFromLot(GNCLot *lot)
Given a LOT, find and return the Invoice attached to the lot.
#define PINFO(format, args...)
Print an informational note.
Definition qoflog.h:256
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition qoflog.h:282
#define PWARN(format, args...)
Log a warning.
Definition qoflog.h:250
#define ENTER(format, args...)
Print a function entry debugging message.
Definition qoflog.h:272
void gnc_lot_add_split(GNCLot *lot, Split *split)
Adds a split to this lot.
Definition gnc-lot.cpp:579
Account * gnc_lot_get_account(const GNCLot *lot)
Returns the account with which this lot is associated.
Definition gnc-lot.cpp:377
void gnc_lot_remove_split(GNCLot *lot, Split *split)
Adds a split from this lot.
Definition gnc-lot.cpp:631
SplitList * gnc_lot_get_split_list(const GNCLot *lot)
Returns a list of all the splits in this lot.
Definition gnc-lot.cpp:425
Split * gnc_lot_get_earliest_split(GNCLot *lot)
Convenience routine to identify the earliest date in the lot.
Definition gnc-lot.cpp:658
#define GNC_DENOM_AUTO
Values that can be passed as the 'denom' argument.
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.
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
int gnc_numeric_compare(gnc_numeric a, gnc_numeric b)
Returns 1 if a>b, -1 if b>a, 0 if a == b
gnc_numeric gnc_numeric_neg(gnc_numeric a)
Returns a newly created gnc_numeric that is the negative of the given gnc_numeric value.
gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
Equivalence predicate: Returns TRUE (1) if a and b represent the same number.
gboolean gnc_numeric_positive_p(gnc_numeric a)
Returns 1 if a > 0, otherwise returns 0.
gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Return a+b.
void gncOwnerAttachToLot(const GncOwner *owner, GNCLot *lot)
Attach an owner to a lot.
Definition gncOwner.c:622
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
gboolean gncOwnerReduceSplitTo(Split *split, gnc_numeric target_amount)
Helper function to reduce the amount of a split to target_amount.
Definition gncOwner.c:959
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
gboolean gncScrubBusinessSplit(Split *split)
The gncScrubBusinessSplit() function will fix all issues found with the given split.
void gncScrubBusinessAccountTree(Account *acc, QofPercentageFunc percentagefunc)
The gncScrubBusinessAccountTreeLots() function will call gncScrubBusinessAccount() on the given accou...
void gncScrubBusinessAccount(Account *acc, QofPercentageFunc percentagefunc)
The gncScrubBusinessAccount() function will call all scrub functions relevant for a given account on ...
void gncScrubBusinessAccountSplits(Account *acc, QofPercentageFunc percentagefunc)
The gncScrubBusinessAccountSplits() function will call gncScrubBusinessSplit() on each split in the g...
gboolean xaccScrubMergeLotSubSplits(GNCLot *lot, gboolean strict)
The xaccScrubMergeLotSubSplits() routine does the same as the xaccScrubMergSubSplits,...
Definition Scrub2.cpp:390
void gncScrubBusinessAccountLots(Account *acc, QofPercentageFunc percentagefunc)
The gncScrubBusinessAccountLots() function will call gncScrubBusinessLot() on each lot in the given a...
gboolean gncScrubBusinessLot(GNCLot *lot)
The gncScrubBusinessLot() function makes sure that the indicated lot has all the correct properties r...
Transaction * xaccSplitGetParent(const Split *split)
Returns the parent transaction of the split.
gboolean xaccSplitDestroy(Split *split)
Destructor.
Definition Split.cpp:1506
void xaccSplitSetMemo(Split *split, const char *memo)
The memo is an arbitrary string associated with a split.
GNCLot * xaccSplitGetLot(const Split *split)
Returns the pointer to the debited/credited Lot where this split belongs to, or NULL if it doesn't be...
Definition Split.cpp:1920
gnc_numeric xaccSplitGetValue(const Split *split)
Returns the value of this split in the transaction's commodity.
gnc_numeric xaccSplitGetAmount(const Split *split)
Returns the amount of the split in the account's commodity.
Implement Accounting Policy Private header File.
STRUCTS.