GnuCash c935c2f+
Loading...
Searching...
No Matches
Scrub.cpp
1/********************************************************************\
2 * Scrub.c -- convert single-entry accounts into clean double-entry *
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\********************************************************************/
22
23/*
24 * FILE:
25 * Scrub.c
26 *
27 * FUNCTION:
28 * Provides a set of functions and utilities for scrubbing clean
29 * single-entry accounts so that they can be promoted into
30 * self-consistent, clean double-entry accounts.
31 *
32 * HISTORY:
33 * Created by Linas Vepstas December 1998
34 * Copyright (c) 1998-2000, 2003 Linas Vepstas <linas@linas.org>
35 * Copyright (c) 2002 Christian Stimming
36 * Copyright (c) 2006 David Hampton
37 */
38
39#include <config.h>
40
41#include <glib.h>
42#include <glib/gi18n.h>
43#include <stdio.h>
44#include <string.h>
45#include <stdint.h>
46#include <stdbool.h>
47#include <unordered_set>
48
49#include "Account.h"
50#include "AccountP.hpp"
51#include "Account.hpp"
52#include "Scrub.h"
53#include "Transaction.h"
54#include "TransactionP.hpp"
55#include "gnc-commodity.h"
56#include "qofinstance-p.h"
57#include "gnc-session.h"
58
59#undef G_LOG_DOMAIN
60#define G_LOG_DOMAIN "gnc.engine.scrub"
61
62static QofLogModule log_module = G_LOG_DOMAIN;
63static gboolean abort_now = FALSE;
64static gint scrub_depth = 0;
65
66
67static Account* xaccScrubUtilityGetOrMakeAccount (Account *root,
68 gnc_commodity* currency,
69 const char* accname,
70 GNCAccountType acctype,
71 gboolean placeholder,
72 gboolean checkname);
73
74void
75gnc_set_abort_scrub (gboolean abort)
76{
77 abort_now = abort;
78}
79
80gboolean
81gnc_get_abort_scrub (void)
82{
83 return abort_now;
84}
85
86gboolean
88{
89 return scrub_depth > 0;
90}
91
92/* ================================================================ */
93
94using TransSet = std::unordered_set<Transaction*>;
95
96static TransSet
97get_all_transactions (Account *account, bool descendants)
98{
99 TransSet set;
100 auto add_transactions = [&set](auto a)
101 { gnc_account_foreach_split (a, [&set](auto s){ set.insert (xaccSplitGetParent (s)); }); };
102 add_transactions (account);
103 if (descendants)
104 gnc_account_foreach_descendant (account, add_transactions);
105 return set;
106}
107
108/* ================================================================ */
109
110static void
111TransScrubOrphansFast (Transaction *trans, Account *root)
112{
113 g_return_if_fail (trans && trans->common_currency && root);
114
115 for (GList *node = trans->splits; node; node = node->next)
116 {
117 Split *split = GNC_SPLIT(node->data);
118 if (abort_now) break;
119
120 if (split->acc) continue;
121
122 DEBUG ("Found an orphan\n");
123
124 gchar *accname = g_strconcat
125 (_("Orphan"), "-", gnc_commodity_get_mnemonic (trans->common_currency),
126 nullptr);
127
128 Account *orph = xaccScrubUtilityGetOrMakeAccount
129 (root, trans->common_currency, accname, ACCT_TYPE_BANK, false, true);
130
131 g_free (accname);
132 if (!orph) continue;
133
134 xaccSplitSetAccount(split, orph);
135 }
136}
137
138static void
139AccountScrubOrphans (Account *acc, bool descendants, QofPercentageFunc percentagefunc)
140{
141 if (!acc) return;
142 scrub_depth++;
143
144 auto transactions = get_all_transactions (acc, descendants);
145 auto total_trans = transactions.size();
146 const char *message = _("Looking for orphans in transaction: %u of %zu");
147 guint current_trans = 0;
148
149 for (auto trans : transactions)
150 {
151 if (current_trans % 10 == 0)
152 {
153 char *progress_msg = g_strdup_printf (message, current_trans, total_trans);
154 (percentagefunc)(progress_msg, (100 * current_trans) / total_trans);
155 g_free (progress_msg);
156 if (abort_now) break;
157 }
158
159 TransScrubOrphansFast (trans, gnc_account_get_root (acc));
160 current_trans++;
161 }
162 (percentagefunc)(nullptr, -1.0);
163 scrub_depth--;
164}
165
166void
168{
169 AccountScrubOrphans (acc, false, percentagefunc);
170}
171
172void
174{
175 AccountScrubOrphans (acc, true, percentagefunc);
176}
177
178void
179xaccTransScrubOrphans (Transaction *trans)
180{
181 SplitList *node;
182 QofBook *book = nullptr;
183 Account *root = nullptr;
184
185 if (!trans) return;
186
187 for (node = trans->splits; node; node = node->next)
188 {
189 Split *split = GNC_SPLIT(node->data);
190 if (abort_now) break;
191
192 if (split->acc)
193 {
194 TransScrubOrphansFast (trans, gnc_account_get_root(split->acc));
195 return;
196 }
197 }
198
199 /* If we got to here, then *none* of the splits belonged to an
200 * account. Not a happy situation. We should dig an account
201 * out of the book the transaction belongs to.
202 * XXX we should probably *always* to this, instead of the above loop!
203 */
204 PINFO ("Free Floating Transaction!");
205 book = xaccTransGetBook (trans);
206 root = gnc_book_get_root_account (book);
207 TransScrubOrphansFast (trans, root);
208}
209
210/* ================================================================ */
211
212void
213xaccAccountTreeScrubSplits (Account *account)
214{
215 if (!account) return;
216
217 xaccAccountScrubSplits (account);
218 gnc_account_foreach_descendant(account,
219 (AccountCb)xaccAccountScrubSplits, nullptr);
220}
221
222void
223xaccAccountScrubSplits (Account *account)
224{
225 scrub_depth++;
226 for (auto s : xaccAccountGetSplits (account))
227 {
228 if (abort_now) break;
229 xaccSplitScrub (s);
230 }
231 scrub_depth--;
232}
233
234/* if dry_run is true, this function will analyze the split and
235 return true if the split will be modified during the actual scrub. */
236static bool
237split_scrub_or_dry_run (Split *split, bool dry_run)
238{
239 Account *account;
240 Transaction *trans;
241 gnc_numeric value, amount;
242 gnc_commodity *currency, *acc_commodity;
243 int scu;
244
245 if (!split) return false;
246 ENTER ("(split=%p)", split);
247
248 trans = xaccSplitGetParent (split);
249 if (!trans)
250 {
251 LEAVE("no trans");
252 return false;
253 }
254
255 account = xaccSplitGetAccount (split);
256
257 /* If there's no account, this split is an orphan.
258 * We need to fix that first, before proceeding.
259 */
260 if (!account)
261 {
262 if (dry_run)
263 return true;
264 else
265 xaccTransScrubOrphans (trans);
266 account = xaccSplitGetAccount (split);
267 }
268
269 /* Grrr... the register gnc_split_register_load() line 203 of
270 * src/register/ledger-core/split-register-load.c will create
271 * free-floating bogus transactions. Ignore these for now ...
272 */
273 if (!account)
274 {
275 PINFO ("Free Floating Transaction!");
276 LEAVE ("no account");
277 return false;
278 }
279
280 /* Split amounts and values should be valid numbers */
281 value = xaccSplitGetValue (split);
282 if (gnc_numeric_check (value))
283 {
284 value = gnc_numeric_zero();
285 if (dry_run)
286 return true;
287 else
288 xaccSplitSetValue (split, value);
289 }
290
291 amount = xaccSplitGetAmount (split);
292 if (gnc_numeric_check (amount))
293 {
294 amount = gnc_numeric_zero();
295 if (dry_run)
296 return true;
297 else
298 xaccSplitSetAmount (split, amount);
299 }
300
301 currency = xaccTransGetCurrency (trans);
302
303 /* If the account doesn't have a commodity,
304 * we should attempt to fix that first.
305 */
306 acc_commodity = xaccAccountGetCommodity(account);
307 if (!acc_commodity)
308 {
309 if (dry_run)
310 return true;
311 else
313 }
314 if (!acc_commodity || !gnc_commodity_equiv(acc_commodity, currency))
315 {
316 LEAVE ("(split=%p) inequiv currency", split);
317 return false;
318 }
319
320 scu = MIN (xaccAccountGetCommoditySCU (account),
321 gnc_commodity_get_fraction (currency));
322
323 if (gnc_numeric_same (amount, value, scu, GNC_HOW_RND_ROUND_HALF_UP))
324 {
325 LEAVE("(split=%p) different values", split);
326 return false;
327 }
328
329 if (dry_run)
330 return true;
331
332 /*
333 * This will be hit every time you answer yes to the dialog "The
334 * current transaction has changed. Would you like to record it.
335 */
336 PINFO ("Adjusted split with mismatched values, desc=\"%s\" memo=\"%s\""
337 " old amount %s %s, new amount %s",
338 trans->description, split->memo,
342
343 xaccTransBeginEdit (trans);
344 xaccSplitSetAmount (split, value);
345 xaccTransCommitEdit (trans);
346 LEAVE ("(split=%p)", split);
347 return true;
348}
349
350/* ================================================================ */
351
352
353static void
354AccountScrubImbalance (Account *acc, bool descendants,
355 QofPercentageFunc percentagefunc)
356{
357 const char *message = _("Looking for imbalances in transaction date %s: %u of %zu");
358
359 if (!acc) return;
360
361 QofBook *book = qof_session_get_book (gnc_get_current_session ());
362 Account *root = gnc_book_get_root_account (book);
363 auto transactions = get_all_transactions (acc, descendants);
364 auto count = transactions.size();
365 auto curr_trans = 0;
366
367 scrub_depth++;
368 for (auto trans : transactions)
369 {
370 if (abort_now) break;
371
372 PINFO("Start processing transaction %d of %zu", curr_trans + 1, count);
373
374 if (curr_trans % 10 == 0)
375 {
376 char *date = qof_print_date (xaccTransGetDate (trans));
377 char *progress_msg = g_strdup_printf (message, date, curr_trans, count);
378 (percentagefunc)(progress_msg, (100 * curr_trans) / count);
379 g_free (progress_msg);
380 g_free (date);
381 }
382
383 TransScrubOrphansFast (trans, root);
385 xaccTransScrubImbalance (trans, root, nullptr);
386
387 PINFO("Finished processing transaction %d of %zu", curr_trans + 1, count);
388 curr_trans++;
389 }
390 (percentagefunc)(nullptr, -1.0);
391 scrub_depth--;
392}
393
394void
395xaccTransScrubSplits (Transaction *trans)
396{
397 if (!trans) return;
398
399 gnc_commodity *currency = xaccTransGetCurrency (trans);
400 if (!currency)
401 PERR ("Transaction doesn't have a currency!");
402
403 bool must_scrub = false;
404
405 for (GList *n = xaccTransGetSplitList (trans); !must_scrub && n; n = g_list_next (n))
406 if (split_scrub_or_dry_run (GNC_SPLIT(n->data), true))
407 must_scrub = true;
408
409 if (!must_scrub)
410 return;
411
412 xaccTransBeginEdit(trans);
413 /* The split scrub expects the transaction to have a currency! */
414
415 for (GList *n = xaccTransGetSplitList (trans); n; n = g_list_next (n))
416 xaccSplitScrub (GNC_SPLIT(n->data));
417
418 xaccTransCommitEdit(trans);
419}
420
421/* ================================================================ */
422
423void
424xaccSplitScrub (Split *split)
425{
426 split_scrub_or_dry_run (split, false);
427}
428
429/* ================================================================ */
430
431
432void
433xaccAccountTreeScrubImbalance (Account *acc, QofPercentageFunc percentagefunc)
434{
435 AccountScrubImbalance (acc, true, percentagefunc);
436}
437
438void
439xaccAccountScrubImbalance (Account *acc, QofPercentageFunc percentagefunc)
440{
441 AccountScrubImbalance (acc, false, percentagefunc);
442}
443
444static Split *
445get_balance_split (Transaction *trans, Account *root, Account *account,
446 gnc_commodity *commodity)
447{
448 Split *balance_split;
449 gchar *accname;
450
451 if (!account ||
452 !gnc_commodity_equiv (commodity, xaccAccountGetCommodity(account)))
453 {
454 if (!root)
455 {
456 root = gnc_book_get_root_account (xaccTransGetBook (trans));
457 if (nullptr == root)
458 {
459 /* This can't occur, things should be in books */
460 PERR ("Bad data corruption, no root account in book");
461 return nullptr;
462 }
463 }
464 accname = g_strconcat (_("Imbalance"), "-",
465 gnc_commodity_get_mnemonic (commodity), nullptr);
466 account = xaccScrubUtilityGetOrMakeAccount (root, commodity,
467 accname, ACCT_TYPE_BANK,
468 FALSE, TRUE);
469 g_free (accname);
470 if (!account)
471 {
472 PERR ("Can't get balancing account");
473 return nullptr;
474 }
475 }
476
477 balance_split = xaccTransFindSplitByAccount(trans, account);
478
479 /* Put split into account before setting split value */
480 if (!balance_split)
481 {
482 balance_split = xaccMallocSplit (qof_instance_get_book(trans));
483
484 xaccTransBeginEdit (trans);
485 xaccSplitSetParent(balance_split, trans);
486 xaccSplitSetAccount(balance_split, account);
487 xaccTransCommitEdit (trans);
488 }
489
490 return balance_split;
491}
492
493static gnc_commodity*
494find_root_currency(void)
495{
496 QofSession *sess = gnc_get_current_session ();
497 Account *root = gnc_book_get_root_account (qof_session_get_book (sess));
498 gnc_commodity *root_currency = xaccAccountGetCommodity (root);
499
500 /* Some older books may not have a currency set on the root
501 * account. In that case find the first top-level INCOME account
502 * and use its currency. */
503 if (!root_currency)
504 {
505 GList *children = gnc_account_get_children (root);
506 for (GList *node = children; node && !root_currency;
507 node = g_list_next (node))
508 {
509 Account *child = GNC_ACCOUNT (node->data);
510 if (xaccAccountGetType (child) == ACCT_TYPE_INCOME)
511 root_currency = xaccAccountGetCommodity (child);
512 }
513 g_list_free (children);
514 }
515 return root_currency;
516}
517
518/* Get the trading split for a given commodity, creating it (and the
519 necessary parent accounts) if it doesn't exist. */
520static Split *
521get_trading_split (Transaction *trans, Account *base,
522 gnc_commodity *commodity)
523{
524 Split *balance_split;
525 Account *trading_account;
526 Account *ns_account;
527 Account *account;
528 Account* root = gnc_book_get_root_account (xaccTransGetBook (trans));
529
530 trading_account = xaccScrubUtilityGetOrMakeAccount (root,
531 nullptr,
532 _("Trading"),
533 ACCT_TYPE_TRADING,
534 TRUE, FALSE);
535 if (!trading_account)
536 {
537 PERR ("Can't get trading account");
538 return nullptr;
539 }
540
541 ns_account = xaccScrubUtilityGetOrMakeAccount (trading_account,
542 nullptr,
544 ACCT_TYPE_TRADING,
545 TRUE, TRUE);
546 if (!ns_account)
547 {
548 PERR ("Can't get namespace account");
549 return nullptr;
550 }
551
552 account = xaccScrubUtilityGetOrMakeAccount (ns_account, commodity,
554 ACCT_TYPE_TRADING,
555 FALSE, FALSE);
556 if (!account)
557 {
558 PERR ("Can't get commodity account");
559 return nullptr;
560 }
561
562
563 balance_split = xaccTransFindSplitByAccount(trans, account);
564
565 /* Put split into account before setting split value */
566 if (!balance_split)
567 {
568 balance_split = xaccMallocSplit (qof_instance_get_book(trans));
569 xaccDisableDataScrubbing();
570
571 xaccTransBeginEdit (trans);
572 xaccSplitSetParent(balance_split, trans);
573 xaccSplitSetAccount(balance_split, account);
574 xaccTransCommitEdit (trans);
575 xaccEnableDataScrubbing();
576 }
577
578 return balance_split;
579}
580
581static void
582add_balance_split (Transaction *trans, gnc_numeric imbalance,
583 Account *root, Account *account)
584{
585 const gnc_commodity *commodity;
586 gnc_numeric old_value, new_value;
587 Split *balance_split;
588 gnc_commodity *currency = xaccTransGetCurrency (trans);
589
590 balance_split = get_balance_split(trans, root, account, currency);
591 if (!balance_split)
592 {
593 /* Error already logged */
594 LEAVE("");
595 return;
596 }
597
598 old_value = xaccSplitGetValue (balance_split);
599
600 /* Note: We have to round for the commodity's fraction, NOT any
601 * already existing denominator (bug #104343), because either one
602 * of the denominators might already be reduced. */
603 new_value = gnc_numeric_sub (old_value, imbalance,
605 GNC_HOW_RND_ROUND_HALF_UP);
606
607 if (gnc_numeric_zero_p (new_value))
608 {
609 const char *p;
610 p = xaccSplitGetMemo (balance_split);
611 if (!p || !*p)
612 {
613 p = xaccSplitGetAction (balance_split);
614 if (!p || !*p)
615 {
616 xaccSplitDestroy (balance_split);
617 return;
618 }
619 }
620 }
621
622 xaccTransBeginEdit (trans);
623 xaccSplitSetValue (balance_split, new_value);
624
625 account = xaccSplitGetAccount(balance_split);
626 commodity = xaccAccountGetCommodity (account);
627 if (gnc_commodity_equiv (currency, commodity))
628 {
629 xaccSplitSetAmount (balance_split, new_value);
630 }
631
632 xaccSplitScrub (balance_split);
633 xaccTransCommitEdit (trans);
634}
635
636/* Balance a transaction without trading accounts. */
637static void
638gnc_transaction_balance_no_trading (Transaction *trans, Account *root,
639 Account *account)
640{
641 gnc_numeric imbalance = xaccTransGetImbalanceValue (trans);
642
643 /* Make the value sum to zero */
644 if (! gnc_numeric_zero_p (imbalance))
645 {
646 PINFO ("Value unbalanced transaction");
647
648 add_balance_split (trans, imbalance, root, account);
649 }
650
651}
652
653static gnc_numeric
654gnc_transaction_get_commodity_imbalance (Transaction *trans,
655 gnc_commodity *commodity)
656{
657 /* Find the value imbalance in this commodity */
658 gnc_numeric val_imbalance = gnc_numeric_zero();
659 GList *splits = nullptr;
660 for (splits = trans->splits; splits; splits = splits->next)
661 {
662 Split *split = GNC_SPLIT(splits->data);
663 gnc_commodity *split_commodity =
665 if (xaccTransStillHasSplit (trans, split) &&
666 gnc_commodity_equal (commodity, split_commodity))
667 val_imbalance = gnc_numeric_add (val_imbalance,
668 xaccSplitGetValue (split),
670 GNC_HOW_DENOM_EXACT);
671 }
672 return val_imbalance;
673}
674
675/* GFunc wrapper for xaccSplitDestroy */
676static void
677destroy_split (void* ptr)
678{
679 Split *split = GNC_SPLIT (ptr);
680 if (split)
681 xaccSplitDestroy (split);
682}
683
684/* Balancing transactions with trading accounts works best when
685 * starting with no trading splits.
686 */
687static void
688xaccTransClearTradingSplits (Transaction *trans)
689{
690 GList *trading_splits = nullptr;
691
692 for (GList* node = trans->splits; node; node = node->next)
693 {
694 Split* split = GNC_SPLIT(node->data);
695 Account* acc = nullptr;
696 if (!split)
697 continue;
698 acc = xaccSplitGetAccount(split);
699 if (acc && xaccAccountGetType(acc) == ACCT_TYPE_TRADING)
700 trading_splits = g_list_prepend (trading_splits, node->data);
701 }
702
703 if (!trading_splits)
704 return;
705
706 xaccTransBeginEdit (trans);
707 /* destroy_splits doesn't actually free the splits but this gets
708 * the list itself freed.
709 */
710 g_list_free_full (trading_splits, destroy_split);
711 xaccTransCommitEdit (trans);
712}
713
714static void
715gnc_transaction_balance_trading (Transaction *trans, Account *root)
716{
717 MonetaryList *imbal_list;
718 MonetaryList *imbalance_commod;
719 Split *balance_split = nullptr;
720
721 /* If the transaction is balanced, nothing more to do */
722 imbal_list = xaccTransGetImbalance (trans);
723 if (!imbal_list)
724 {
725 LEAVE("transaction is balanced");
726 return;
727 }
728
729 PINFO ("Currency unbalanced transaction");
730
731 for (imbalance_commod = imbal_list; imbalance_commod;
732 imbalance_commod = imbalance_commod->next)
733 {
734 auto imbal_mon = static_cast<gnc_monetary*>(imbalance_commod->data);
735 gnc_commodity *commodity;
736 gnc_numeric old_amount, new_amount;
737 const gnc_commodity *txn_curr = xaccTransGetCurrency (trans);
738
739 commodity = gnc_monetary_commodity (*imbal_mon);
740
741 balance_split = get_trading_split(trans, root, commodity);
742 if (!balance_split)
743 {
744 /* Error already logged */
745 gnc_monetary_list_free(imbal_list);
746 LEAVE("");
747 return;
748 }
749
750 xaccTransBeginEdit (trans);
751
752 old_amount = xaccSplitGetAmount (balance_split);
753 new_amount = gnc_numeric_sub (old_amount, gnc_monetary_value(*imbal_mon),
755 GNC_HOW_RND_ROUND_HALF_UP);
756
757 xaccSplitSetAmount (balance_split, new_amount);
758
759 if (gnc_commodity_equal (txn_curr, commodity))
760 {
761 /* Imbalance commodity is the transaction currency, value in the
762 split must be the same as the amount */
763 xaccSplitSetValue (balance_split, new_amount);
764 }
765 else
766 {
767 gnc_numeric val_imbalance = gnc_transaction_get_commodity_imbalance (trans, commodity);
768
769 gnc_numeric old_value = xaccSplitGetValue (balance_split);
770 gnc_numeric new_value = gnc_numeric_sub (old_value, val_imbalance,
772 GNC_HOW_RND_ROUND_HALF_UP);
773
774 xaccSplitSetValue (balance_split, new_value);
775 }
776
777 xaccSplitScrub (balance_split);
778 xaccTransCommitEdit (trans);
779 }
780
781 gnc_monetary_list_free(imbal_list);
782}
783
789static void
790gnc_transaction_balance_trading_more_splits (Transaction *trans, Account *root)
791{
792 /* Copy the split list so we don't see the splits we're adding */
793 GList *splits_dup = g_list_copy(trans->splits), *splits = nullptr;
794 const gnc_commodity *txn_curr = xaccTransGetCurrency (trans);
795 for (splits = splits_dup; splits; splits = splits->next)
796 {
797 Split *split = GNC_SPLIT(splits->data);
798 if (! xaccTransStillHasSplit(trans, split)) continue;
801 {
802 gnc_commodity *commodity;
803 gnc_numeric old_value, new_value;
804 Split *balance_split;
805
807 if (!commodity)
808 {
809 PERR("Split has no commodity");
810 continue;
811 }
812 balance_split = get_trading_split(trans, root, commodity);
813 if (!balance_split)
814 {
815 /* Error already logged */
816 LEAVE("");
817 return;
818 }
819 xaccTransBeginEdit (trans);
820
821 old_value = xaccSplitGetValue (balance_split);
822 new_value = gnc_numeric_sub (old_value, xaccSplitGetValue(split),
824 GNC_HOW_RND_ROUND_HALF_UP);
825 xaccSplitSetValue (balance_split, new_value);
826
827 /* Don't change the balance split's amount since the amount
828 is zero in the split we're working on */
829
830 xaccSplitScrub (balance_split);
831 xaccTransCommitEdit (trans);
832 }
833 }
834
835 g_list_free(splits_dup);
836}
837
844void
845xaccTransScrubImbalance (Transaction *trans, Account *root,
846 Account *account)
847{
848 gnc_numeric imbalance;
849
850 if (!trans) return;
851
852 ENTER ("()");
853
854 /* Must look for orphan splits even if there is no imbalance. */
855 xaccTransScrubSplits (trans);
856
857 /* Return immediately if things are balanced. */
858 if (xaccTransIsBalanced (trans))
859 {
860 LEAVE ("transaction is balanced");
861 return;
862 }
863
864 if (! xaccTransUseTradingAccounts (trans))
865 {
866 gnc_transaction_balance_no_trading (trans, root, account);
867 LEAVE ("transaction balanced, no managed trading accounts");
868 return;
869 }
870
871 xaccTransClearTradingSplits (trans);
872 imbalance = xaccTransGetImbalanceValue (trans);
873 if (! gnc_numeric_zero_p (imbalance))
874 {
875 PINFO ("Value unbalanced transaction");
876
877 add_balance_split (trans, imbalance, root, account);
878 }
879
880 gnc_transaction_balance_trading (trans, root);
882 {
883 LEAVE ("()");
884 return;
885 }
886 /* If the transaction is still not balanced, it's probably because there
887 are splits with zero amount and non-zero value. These are usually
888 realized gain/loss splits. Add a reversing split for each of them to
889 balance the value. */
890
891 gnc_transaction_balance_trading_more_splits (trans, root);
893 PERR("Balancing currencies unbalanced value");
894
895}
896
897/* ================================================================ */
898/* The xaccTransFindCommonCurrency () method returns
899 * a gnc_commodity indicating a currency denomination that all
900 * of the splits in this transaction have in common, using the
901 * old/obsolete currency/security fields of the split accounts.
902 */
903
904static gnc_commodity *
905FindCommonExclSCurrency (SplitList *splits,
906 gnc_commodity * ra, gnc_commodity * rb,
907 Split *excl_split)
908{
909 GList *node;
910
911 if (!splits) return nullptr;
912
913 for (node = splits; node; node = node->next)
914 {
915 Split *s = GNC_SPLIT(node->data);
916 gnc_commodity * sa, * sb;
917
918 if (s == excl_split) continue;
919
920 g_return_val_if_fail (s->acc, nullptr);
921
922 sa = DxaccAccountGetCurrency (s->acc);
923 sb = xaccAccountGetCommodity (s->acc);
924
925 if (ra && rb)
926 {
927 int aa = !gnc_commodity_equiv(ra, sa);
928 int ab = !gnc_commodity_equiv(ra, sb);
929 int ba = !gnc_commodity_equiv(rb, sa);
930 int bb = !gnc_commodity_equiv(rb, sb);
931
932 if ( (!aa) && bb) rb = nullptr;
933 else if ( (!ab) && ba) rb = nullptr;
934 else if ( (!ba) && ab) ra = nullptr;
935 else if ( (!bb) && aa) ra = nullptr;
936 else if ( aa && bb && ab && ba )
937 {
938 ra = nullptr;
939 rb = nullptr;
940 }
941
942 if (!ra)
943 {
944 ra = rb;
945 rb = nullptr;
946 }
947 }
948 else if (ra && !rb)
949 {
950 int aa = !gnc_commodity_equiv(ra, sa);
951 int ab = !gnc_commodity_equiv(ra, sb);
952 if ( aa && ab ) ra = nullptr;
953 }
954 else if (!ra && rb)
955 {
956 int aa = !gnc_commodity_equiv(rb, sa);
957 int ab = !gnc_commodity_equiv(rb, sb);
958 ra = ( aa && ab ) ? nullptr : rb;
959 }
960
961 if ((!ra) && (!rb)) return nullptr;
962 }
963
964 return (ra);
965}
966
967/* This is the wrapper for those calls (i.e. the older ones) which
968 * don't exclude one split from the splitlist when looking for a
969 * common currency.
970 */
971static gnc_commodity *
972FindCommonCurrency (GList *splits, gnc_commodity * ra, gnc_commodity * rb)
973{
974 return FindCommonExclSCurrency(splits, ra, rb, nullptr);
975}
976
977static gnc_commodity *
978xaccTransFindOldCommonCurrency (Transaction *trans, QofBook *book)
979{
980 gnc_commodity *ra, *rb, *retval;
981 Split *split;
982
983 if (!trans) return nullptr;
984
985 if (trans->splits == nullptr) return nullptr;
986
987 g_return_val_if_fail (book, nullptr);
988
989 split = GNC_SPLIT(trans->splits->data);
990
991 if (!split || nullptr == split->acc) return nullptr;
992
993 ra = DxaccAccountGetCurrency (split->acc);
994 rb = xaccAccountGetCommodity (split->acc);
995
996 retval = FindCommonCurrency (trans->splits, ra, rb);
997
998 if (retval && !gnc_commodity_is_currency(retval))
999 retval = nullptr;
1000
1001 return retval;
1002}
1003
1004/* Test the currency of the splits and find the most common and return
1005 * it, or nullptr if there is no currency more common than the
1006 * others -- or none at all.
1007 */
1008typedef struct
1009{
1010 gnc_commodity *commodity;
1011 unsigned int count;
1013
1014static gint
1015commodity_equal (gconstpointer a, gconstpointer b)
1016{
1018 gnc_commodity *com = (gnc_commodity*)b;
1019 if ( cc == nullptr || cc->commodity == nullptr ||
1020 !GNC_IS_COMMODITY( cc->commodity ) ) return -1;
1021 if ( com == nullptr || !GNC_IS_COMMODITY( com ) ) return 1;
1022 if ( gnc_commodity_equal(cc->commodity, com) )
1023 return 0;
1024 return 1;
1025}
1026
1027static gint
1028commodity_compare( gconstpointer a, gconstpointer b)
1029{
1030 CommodityCount *ca = (CommodityCount*)a, *cb = (CommodityCount*)b;
1031 if (ca == nullptr || ca->commodity == nullptr ||
1032 !GNC_IS_COMMODITY( ca->commodity ) )
1033 {
1034 if (cb == nullptr || cb->commodity == nullptr ||
1035 !GNC_IS_COMMODITY( cb->commodity ) )
1036 return 0;
1037 return -1;
1038 }
1039 if (cb == nullptr || cb->commodity == nullptr ||
1040 !GNC_IS_COMMODITY( cb->commodity ) )
1041 return 1;
1042 if (ca->count == cb->count)
1043 return 0;
1044 return ca->count > cb->count ? 1 : -1;
1045}
1046
1047/* Find the commodities in the account of each of the splits of a
1048 * transaction, and rank them by how many splits in which they
1049 * occur. Commodities which are currencies count more than those which
1050 * aren't, because for simple buy and sell transactions it makes
1051 * slightly more sense for the transaction commodity to be the
1052 * currency -- to the extent that it makes sense for a transaction to
1053 * have a currency at all. jralls, 2010-11-02 */
1054
1055static gnc_commodity *
1056xaccTransFindCommonCurrency (Transaction *trans, QofBook *book)
1057{
1058 gnc_commodity *com_scratch;
1059 GList *node = nullptr;
1060 GSList *comlist = nullptr, *found = nullptr;
1061
1062 if (!trans) return nullptr;
1063
1064 if (trans->splits == nullptr) return nullptr;
1065
1066 g_return_val_if_fail (book, nullptr);
1067
1068 /* Find the most commonly used currency among the splits. If a given split
1069 is in a non-currency commodity, then look for an ancestor account in a
1070 currency, but prefer currencies used directly in splits. Ignore trading
1071 account splits in this whole process, they don't add any value to this algorithm. */
1072 for (node = trans->splits; node; node = node->next)
1073 {
1074 Split *s = GNC_SPLIT(node->data);
1075 unsigned int curr_weight;
1076
1077 if (s == nullptr || s->acc == nullptr) continue;
1078 if (xaccAccountGetType(s->acc) == ACCT_TYPE_TRADING) continue;
1079 com_scratch = xaccAccountGetCommodity(s->acc);
1080 if (com_scratch && gnc_commodity_is_currency(com_scratch))
1081 {
1082 curr_weight = 3;
1083 }
1084 else
1085 {
1086 com_scratch = gnc_account_get_currency_or_parent(s->acc);
1087 if (com_scratch == nullptr) continue;
1088 curr_weight = 1;
1089 }
1090 if ( comlist )
1091 {
1092 found = g_slist_find_custom(comlist, com_scratch, commodity_equal);
1093 }
1094 if (comlist == nullptr || found == nullptr)
1095 {
1096 CommodityCount *count = g_slice_new0(CommodityCount);
1097 count->commodity = com_scratch;
1098 count->count = curr_weight;
1099 comlist = g_slist_append(comlist, count);
1100 }
1101 else
1102 {
1103 CommodityCount *count = (CommodityCount*)(found->data);
1104 count->count += curr_weight;
1105 }
1106 }
1107 found = g_slist_sort( comlist, commodity_compare);
1108
1109 if ( found && found->data && (((CommodityCount*)(found->data))->commodity != nullptr))
1110 {
1111 return ((CommodityCount*)(found->data))->commodity;
1112 }
1113 /* We didn't find a currency in the current account structure, so try
1114 * an old one. */
1115 return xaccTransFindOldCommonCurrency( trans, book );
1116}
1117
1118/* ================================================================ */
1119
1120void
1121xaccTransScrubCurrency (Transaction *trans)
1122{
1123 SplitList *node;
1124 gnc_commodity *currency;
1125
1126 if (!trans) return;
1127
1128 /* If there are any orphaned splits in a transaction, then the
1129 * this routine will fail. Therefore, we want to make sure that
1130 * there are no orphans (splits without parent account).
1131 */
1132 xaccTransScrubOrphans (trans);
1133
1134 currency = xaccTransGetCurrency (trans);
1135 if (currency && gnc_commodity_is_currency(currency)) return;
1136
1137 currency = xaccTransFindCommonCurrency (trans, qof_instance_get_book(trans));
1138 if (currency)
1139 {
1140 xaccTransBeginEdit (trans);
1141 xaccTransSetCurrency (trans, currency);
1142 xaccTransCommitEdit (trans);
1143 }
1144 else
1145 {
1146 if (nullptr == trans->splits)
1147 {
1148 PWARN ("Transaction \"%s\" has no splits in it!", trans->description);
1149 }
1150 else
1151 {
1152 SplitList *node;
1153 char guid_str[GUID_ENCODING_LENGTH + 1];
1154 guid_to_string_buff(xaccTransGetGUID(trans), guid_str);
1155 PWARN ("no common transaction currency found for trans=\"%s\" (%s);",
1156 trans->description, guid_str);
1157
1158 for (node = trans->splits; node; node = node->next)
1159 {
1160 Split *split = GNC_SPLIT(node->data);
1161 if (nullptr == split->acc)
1162 {
1163 PWARN (" split=\"%s\" is not in any account!", split->memo);
1164 }
1165 else
1166 {
1167 gnc_commodity *currency = xaccAccountGetCommodity(split->acc);
1168 PWARN ("setting to split=\"%s\" account=\"%s\" commodity=\"%s\"",
1169 split->memo, xaccAccountGetName(split->acc),
1170 gnc_commodity_get_mnemonic(currency));
1171
1172 xaccTransBeginEdit (trans);
1173 xaccTransSetCurrency (trans, currency);
1174 xaccTransCommitEdit (trans);
1175 return;
1176 }
1177 }
1178 }
1179 return;
1180 }
1181
1182 for (node = trans->splits; node; node = node->next)
1183 {
1184 Split *sp = GNC_SPLIT(node->data);
1185
1187 xaccSplitGetValue (sp)))
1188 {
1189 gnc_commodity *acc_currency;
1190
1191 acc_currency = sp->acc ? xaccAccountGetCommodity(sp->acc) : nullptr;
1192 if (acc_currency == currency)
1193 {
1194 /* This Split needs fixing: The transaction-currency equals
1195 * the account-currency/commodity, but the amount/values are
1196 * inequal i.e. they still correspond to the security
1197 * (amount) and the currency (value). In the new model, the
1198 * value is the amount in the account-commodity -- so it
1199 * needs to be set to equal the amount (since the
1200 * account-currency doesn't exist anymore).
1201 *
1202 * Note: Nevertheless we lose some information here. Namely,
1203 * the information that the 'amount' in 'account-old-security'
1204 * was worth 'value' in 'account-old-currency'. Maybe it would
1205 * be better to store that information in the price database?
1206 * But then, for old currency transactions there is still the
1207 * 'other' transaction, which is going to keep that
1208 * information. So I don't bother with that here. -- cstim,
1209 * 2002/11/20. */
1210
1211 PWARN ("Adjusted split with mismatched values, desc=\"%s\" memo=\"%s\""
1212 " old amount %s %s, new amount %s",
1213 trans->description, sp->memo,
1215 gnc_commodity_get_mnemonic (currency),
1217 xaccTransBeginEdit (trans);
1219 xaccTransCommitEdit (trans);
1220 }
1221 /*else
1222 {
1223 PINFO ("Ok: Split '%s' Amount %s %s, value %s %s",
1224 xaccSplitGetMemo (sp),
1225 gnc_num_dbg_to_string (amount),
1226 gnc_commodity_get_mnemonic (currency),
1227 gnc_num_dbg_to_string (value),
1228 gnc_commodity_get_mnemonic (acc_currency));
1229 }*/
1230 }
1231 }
1232
1233}
1234
1235/* ================================================================ */
1236
1237void
1239{
1240 gnc_commodity *commodity;
1241
1242 if (!account) return;
1243 if (xaccAccountGetType(account) == ACCT_TYPE_ROOT) return;
1244
1245 commodity = xaccAccountGetCommodity (account);
1246 if (commodity) return;
1247
1248 /* Use the 'obsolete' routines to try to figure out what the
1249 * account commodity should have been. */
1250 commodity = xaccAccountGetCommodity (account);
1251 if (commodity)
1252 {
1253 xaccAccountSetCommodity (account, commodity);
1254 return;
1255 }
1256
1257 commodity = DxaccAccountGetCurrency (account);
1258 if (commodity)
1259 {
1260 xaccAccountSetCommodity (account, commodity);
1261 return;
1262 }
1263
1264 PERR ("Account \"%s\" does not have a commodity!",
1265 xaccAccountGetName(account));
1266}
1267
1268/* ================================================================ */
1269
1270/* EFFECTIVE FRIEND FUNCTION declared in qofinstance-p.h */
1271extern void qof_instance_set_dirty (QofInstance*);
1272
1273static void
1274xaccAccountDeleteOldData (Account *account)
1275{
1276 if (!account) return;
1277 xaccAccountBeginEdit (account);
1278 qof_instance_set_kvp (QOF_INSTANCE (account), nullptr, 1, "old-currency");
1279 qof_instance_set_kvp (QOF_INSTANCE (account), nullptr, 1, "old-security");
1280 qof_instance_set_kvp (QOF_INSTANCE (account), nullptr, 1, "old-currency-scu");
1281 qof_instance_set_kvp (QOF_INSTANCE (account), nullptr, 1, "old-security-scu");
1282 qof_instance_set_dirty (QOF_INSTANCE (account));
1283 xaccAccountCommitEdit (account);
1284}
1285
1286static int
1287scrub_trans_currency_helper (Transaction *t, gpointer data)
1288{
1290 return 0;
1291}
1292
1293static void
1294scrub_account_commodity_helper (Account *account, gpointer data)
1295{
1296 scrub_depth++;
1297 xaccAccountScrubCommodity (account);
1298 xaccAccountDeleteOldData (account);
1299 scrub_depth--;
1300}
1301
1302void
1304{
1305 if (!acc) return;
1306 scrub_depth++;
1307 xaccAccountTreeForEachTransaction (acc, scrub_trans_currency_helper, nullptr);
1308
1309 scrub_account_commodity_helper (acc, nullptr);
1310 gnc_account_foreach_descendant (acc, scrub_account_commodity_helper, nullptr);
1311 scrub_depth--;
1312}
1313
1314/* ================================================================ */
1315
1316static gboolean
1317check_quote_source (gnc_commodity *com, gpointer data)
1318{
1319 gboolean *commodity_has_quote_src = (gboolean *)data;
1320 if (com && !gnc_commodity_is_iso(com))
1321 *commodity_has_quote_src |= gnc_commodity_get_quote_flag(com);
1322 return TRUE;
1323}
1324
1325static void
1326move_quote_source (Account *account, gpointer data)
1327{
1328 gnc_commodity *com;
1329 gnc_quote_source *quote_source;
1330 gboolean new_style = GPOINTER_TO_INT(data);
1331 const char *source, *tz;
1332
1333 com = xaccAccountGetCommodity(account);
1334 if (!com)
1335 return;
1336
1337 if (!new_style)
1338 {
1339 source = dxaccAccountGetPriceSrc(account);
1340 if (!source || !*source)
1341 return;
1342 tz = dxaccAccountGetQuoteTZ(account);
1343
1344 PINFO("to %8s from %s", gnc_commodity_get_mnemonic(com),
1345 xaccAccountGetName(account));
1347 quote_source = gnc_quote_source_lookup_by_internal(source);
1348 if (!quote_source)
1349 quote_source = gnc_quote_source_add_new(source, FALSE);
1350 gnc_commodity_set_quote_source(com, quote_source);
1352 }
1353
1354 dxaccAccountSetPriceSrc(account, nullptr);
1355 dxaccAccountSetQuoteTZ(account, nullptr);
1356 return;
1357}
1358
1359
1360void
1362{
1363 gboolean new_style = FALSE;
1364 ENTER(" ");
1365
1366 if (!root || !table)
1367 {
1368 LEAVE("Oops");
1369 return;
1370 }
1371 scrub_depth++;
1372 gnc_commodity_table_foreach_commodity (table, check_quote_source, &new_style);
1373
1374 move_quote_source(root, GINT_TO_POINTER(new_style));
1375 gnc_account_foreach_descendant (root, move_quote_source,
1376 GINT_TO_POINTER(new_style));
1377 LEAVE("Migration done");
1378 scrub_depth--;
1379}
1380
1381/* ================================================================ */
1382
1383void
1385{
1386 GValue v = G_VALUE_INIT;
1387 gchar *str2;
1388
1389 if (!account) return;
1390 scrub_depth++;
1391
1392 qof_instance_get_kvp (QOF_INSTANCE (account), &v, 1, "notes");
1393 if (G_VALUE_HOLDS_STRING (&v))
1394 {
1395 str2 = g_strstrip(g_value_dup_string(&v));
1396 if (strlen(str2) == 0)
1397 qof_instance_slot_delete (QOF_INSTANCE (account), "notes");
1398 g_free(str2);
1399 }
1400
1401 qof_instance_get_kvp (QOF_INSTANCE (account), &v, 1, "placeholder");
1402 if ((G_VALUE_HOLDS_STRING (&v) &&
1403 strcmp(g_value_get_string (&v), "false") == 0) ||
1404 (G_VALUE_HOLDS_BOOLEAN (&v) && ! g_value_get_boolean (&v)))
1405 qof_instance_slot_delete (QOF_INSTANCE (account), "placeholder");
1406
1407 g_value_unset (&v);
1408 qof_instance_slot_delete_if_empty (QOF_INSTANCE (account), "hbci");
1409 scrub_depth--;
1410}
1411
1412/* ================================================================ */
1413
1414void
1416{
1417 GValue value_s = G_VALUE_INIT;
1418 gboolean already_scrubbed;
1419
1420 // get the run-once value
1421 qof_instance_get_kvp (QOF_INSTANCE (book), &value_s, 1, "remove-color-not-set-slots");
1422
1423 already_scrubbed = (G_VALUE_HOLDS_STRING (&value_s) &&
1424 !g_strcmp0 (g_value_get_string (&value_s), "true"));
1425 g_value_unset (&value_s);
1426
1427 if (already_scrubbed)
1428 return;
1429 else
1430 {
1431 GValue value_b = G_VALUE_INIT;
1432 Account *root = gnc_book_get_root_account (book);
1433 GList *accts = gnc_account_get_descendants_sorted (root);
1434 GList *ptr;
1435
1436 for (ptr = accts; ptr; ptr = g_list_next (ptr))
1437 {
1438 auto acct = GNC_ACCOUNT(ptr->data);
1439 auto color = xaccAccountGetColor (acct);
1440
1441 if (g_strcmp0 (color, "Not Set") == 0)
1442 xaccAccountSetColor (acct, "");
1443 }
1444 g_list_free (accts);
1445
1446 g_value_init (&value_b, G_TYPE_BOOLEAN);
1447 g_value_set_boolean (&value_b, TRUE);
1448
1449 // set the run-once value
1450 qof_instance_set_kvp (QOF_INSTANCE (book), &value_b, 1, "remove-color-not-set-slots");
1451 g_value_unset (&value_b);
1452 }
1453}
1454
1455/* ================================================================ */
1456
1457static Account*
1458construct_account (Account *root, gnc_commodity *currency, const char *accname,
1459 GNCAccountType acctype, gboolean placeholder)
1460{
1461 gnc_commodity* root_currency = find_root_currency ();
1462 Account *acc = xaccMallocAccount(gnc_account_get_book (root));
1464 if (accname && *accname)
1465 xaccAccountSetName (acc, accname);
1466 if (currency || root_currency)
1467 xaccAccountSetCommodity (acc, currency ? currency : root_currency);
1468 xaccAccountSetType (acc, acctype);
1469 xaccAccountSetPlaceholder (acc, placeholder);
1470
1471 /* Hang the account off the root. */
1472 gnc_account_append_child (root, acc);
1474 return acc;
1475}
1476
1477static Account*
1478find_root_currency_account_in_list (GList *acc_list)
1479{
1480 gnc_commodity* root_currency = find_root_currency();
1481 for (GList *node = acc_list; node; node = g_list_next (node))
1482 {
1483 Account *acc = GNC_ACCOUNT (node->data);
1484 gnc_commodity *acc_commodity = nullptr;
1485 if (G_UNLIKELY (!acc)) continue;
1486 acc_commodity = xaccAccountGetCommodity(acc);
1487 if (gnc_commodity_equiv (acc_commodity, root_currency))
1488 return acc;
1489 }
1490
1491 return nullptr;
1492}
1493
1494static Account*
1495find_account_matching_name_in_list (GList *acc_list, const char* accname)
1496{
1497 for (GList* node = acc_list; node; node = g_list_next(node))
1498 {
1499 Account *acc = GNC_ACCOUNT (node->data);
1500 if (G_UNLIKELY (!acc)) continue;
1501 if (g_strcmp0 (accname, xaccAccountGetName (acc)) == 0)
1502 return acc;
1503 }
1504 return nullptr;
1505}
1506
1507Account *
1508xaccScrubUtilityGetOrMakeAccount (Account *root, gnc_commodity * currency,
1509 const char *accname, GNCAccountType acctype,
1510 gboolean placeholder, gboolean checkname)
1511{
1512 GList* acc_list;
1513 Account *acc = nullptr;
1514
1515 g_return_val_if_fail (root, nullptr);
1516
1517 acc_list =
1519 checkname ? accname : nullptr,
1520 acctype, currency);
1521
1522 if (!acc_list)
1523 return construct_account (root, currency, accname,
1524 acctype, placeholder);
1525
1526 if (g_list_next(acc_list))
1527 {
1528 if (!currency)
1529 acc = find_root_currency_account_in_list (acc_list);
1530
1531 if (!acc)
1532 acc = find_account_matching_name_in_list (acc_list, accname);
1533 }
1534
1535 if (!acc)
1536 acc = GNC_ACCOUNT (acc_list->data);
1537
1538 g_list_free (acc_list);
1539 return acc;
1540}
1541
1542void
1543xaccTransScrubPostedDate (Transaction *trans)
1544{
1545 time64 orig = xaccTransGetDate(trans);
1546 if(orig == INT64_MAX)
1547 {
1548 GDate date = xaccTransGetDatePostedGDate(trans);
1549 time64 time = gdate_to_time64(date);
1550 if(time != INT64_MAX)
1551 {
1552 // xaccTransSetDatePostedSecs handles committing the change.
1553 xaccTransSetDatePostedSecs(trans, time);
1554 }
1555 }
1556}
1557
1558/* ==================== END OF FILE ==================== */
This is the private header for the account structure.
Account handling public routines.
Account public routines (C++ api)
convert single-entry accounts to clean double-entry
API for Transactions and Splits (journal entries)
Commodity handling public routines.
const char * xaccAccountGetName(const Account *acc)
Get the account's name.
Definition Account.cpp:3289
void xaccAccountSetColor(Account *acc, const char *str)
Set the account's Color.
Definition Account.cpp:2613
const char * xaccAccountGetColor(const Account *acc)
Get the account's color.
Definition Account.cpp:3350
void xaccAccountCommitEdit(Account *acc)
ThexaccAccountCommitEdit() subroutine is the second phase of a two-phase-commit wrapper for account u...
Definition Account.cpp:1516
Account * gnc_account_get_root(Account *acc)
This routine returns the root account of the account tree that the specified account belongs to.
Definition Account.cpp:2942
GList * gnc_account_get_descendants_sorted(const Account *account)
This function returns a GList containing all the descendants of the specified account,...
Definition Account.cpp:3052
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
The account types are used to determine how the transaction data in the account is displayed.
Definition Account.h:103
gnc_commodity * gnc_account_get_currency_or_parent(const Account *account)
Returns a gnc_commodity that is a currency, suitable for being a Transaction's currency.
Definition Account.cpp:3415
void gnc_account_append_child(Account *new_parent, Account *child)
This function will remove from the child account any pre-existing parent relationship,...
Definition Account.cpp:2836
GNCAccountType xaccAccountGetType(const Account *acc)
Returns the account's account type.
Definition Account.cpp:3267
int xaccAccountGetCommoditySCU(const Account *acc)
Return the SCU for the account.
Definition Account.cpp:2745
void xaccAccountSetName(Account *acc, const char *str)
Set the account's name.
Definition Account.cpp:2458
void dxaccAccountSetPriceSrc(Account *acc, const char *src)
Set a string that identifies the Finance::Quote backend that should be used to retrieve online prices...
Definition Account.cpp:4835
GList * gnc_account_get_children(const Account *account)
This routine returns a GList of all children accounts of the specified account.
Definition Account.cpp:2960
void xaccAccountSetType(Account *acc, GNCAccountType tip)
Set the account's type.
Definition Account.cpp:2437
const char * dxaccAccountGetPriceSrc(const Account *acc)
Get a string that identifies the Finance::Quote backend that should be used to retrieve online prices...
Definition Account.cpp:4847
Account * xaccMallocAccount(QofBook *book)
Constructor.
Definition Account.cpp:1270
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account's commodity
Definition Account.cpp:3408
void xaccAccountSetCommodity(Account *acc, gnc_commodity *com)
Set the account's commodity.
Definition Account.cpp:2678
@ ACCT_TYPE_ROOT
The hidden root account of an account tree.
Definition Account.h:153
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
QofBook * qof_session_get_book(const QofSession *session)
Returns the QofBook of this session.
gboolean gnc_commodity_equiv(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equivalent.
gboolean gnc_commodity_equal(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equal.
gnc_quote_source * gnc_quote_source_add_new(const char *source_name, gboolean supported)
Create a new quote source.
const char * gnc_commodity_get_namespace(const gnc_commodity *cm)
Retrieve the namespace for the specified commodity.
int gnc_commodity_get_fraction(const gnc_commodity *cm)
Retrieve the fraction for the specified commodity.
void gnc_commodity_set_quote_flag(gnc_commodity *cm, const gboolean flag)
Set the automatic price quote flag for the specified commodity.
void gnc_commodity_set_quote_source(gnc_commodity *cm, gnc_quote_source *src)
Set the automatic price quote source for the specified commodity.
gnc_quote_source * gnc_quote_source_lookup_by_internal(const char *name)
Given the internal (gnucash or F::Q) name of a quote source, find the data structure identified by th...
void gnc_monetary_list_free(MonetaryList *list)
Free a MonetaryList and all the monetaries it points to.
gboolean gnc_commodity_is_iso(const gnc_commodity *cm)
Checks to see if the specified commodity is an ISO 4217 recognized currency.
const char * gnc_commodity_get_mnemonic(const gnc_commodity *cm)
Retrieve the mnemonic for the specified commodity.
gboolean gnc_commodity_get_quote_flag(const gnc_commodity *cm)
Retrieve the automatic price quote flag for the specified commodity.
void gnc_commodity_set_quote_tz(gnc_commodity *cm, const char *tz)
Set the automatic price quote timezone for the specified commodity.
gboolean gnc_commodity_is_currency(const gnc_commodity *cm)
Checks to see if the specified commodity is an ISO 4217 recognized currency or a legacy currency.
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
time64 gdate_to_time64(GDate d)
Turns a GDate into a time64, returning the first second of the day.
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
Returns the valuation commodity of this transaction.
GDate xaccTransGetDatePostedGDate(const Transaction *trans)
Retrieve the posted date of the transaction.
void xaccTransCommitEdit(Transaction *trans)
The xaccTransCommitEdit() method indicates that the changes to the transaction and its splits are com...
MonetaryList * xaccTransGetImbalance(const Transaction *trans)
The xaccTransGetImbalance method returns a list giving the value of the transaction in each currency ...
GList * gnc_account_lookup_by_type_and_commodity(Account *root, const char *name, GNCAccountType acctype, gnc_commodity *commodity)
Find a direct child account matching name, GNCAccountType, and/or commodity.
Definition Account.cpp:3188
gboolean xaccTransIsBalanced(const Transaction *trans)
Returns true if the transaction is balanced according to the rules currently in effect.
#define xaccTransGetGUID(X)
void dxaccAccountSetQuoteTZ(Account *acc, const char *tz)
Set the timezone to be used when interpreting the results from a given Finance::Quote backend.
Definition Account.cpp:4860
void xaccTransSetDatePostedSecs(Transaction *trans, time64 secs)
The xaccTransSetDatePostedSecs() method will modify the posted date of the transaction,...
void xaccTransSetCurrency(Transaction *trans, gnc_commodity *curr)
Set a new currency on a transaction.
#define xaccTransGetBook(X)
SplitList * xaccTransGetSplitList(const Transaction *trans)
The xaccTransGetSplitList() method returns a GList of the splits in a transaction.
gboolean xaccTransUseTradingAccounts(const Transaction *trans)
Determine whether this transaction should use commodity trading accounts.
void xaccAccountSetPlaceholder(Account *acc, gboolean val)
Set the "placeholder" flag for an account.
Definition Account.cpp:4125
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...
gnc_numeric xaccTransGetImbalanceValue(const Transaction *trans)
The xaccTransGetImbalanceValue() method returns the total value of the transaction.
time64 xaccTransGetDate(const Transaction *trans)
Retrieve the posted date of the transaction.
gnc_commodity * DxaccAccountGetCurrency(const Account *acc)
Definition Account.cpp:3396
const char * dxaccAccountGetQuoteTZ(const Account *acc)
Get the timezone to be used when interpreting the results from a given Finance::Quote backend.
Definition Account.cpp:4871
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
Definition guid.h:84
gchar * guid_to_string_buff(const GncGUID *guid, gchar *str)
The guid_to_string_buff() routine puts a null-terminated string encoding of the id into the memory po...
Definition guid.cpp:208
QofBook * qof_instance_get_book(gconstpointer inst)
Return the book pointer.
void qof_instance_get_kvp(QofInstance *, GValue *value, unsigned count,...)
Retrieves the contents of a KVP slot into a provided GValue.
void qof_instance_set_kvp(QofInstance *, GValue const *value, unsigned count,...)
Sets a KVP slot to a value from a GValue.
#define PINFO(format, args...)
Print an informational note.
Definition qoflog.h:256
#define DEBUG(format, args...)
Print a debugging message.
Definition qoflog.h:264
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition qoflog.h:282
#define PERR(format, args...)
Log a serious error.
Definition qoflog.h:244
#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
gnc_numeric gnc_numeric_sub(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Return a-b.
int gnc_numeric_same(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Equivalence predicate: Convert both a and b to denom using the specified DENOM and method HOW,...
#define GNC_DENOM_AUTO
Values that can be passed as the 'denom' argument.
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
Equivalence predicate: Returns TRUE (1) if a and b represent the same number.
gchar * gnc_num_dbg_to_string(gnc_numeric n)
Convert to string.
gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Return a+b.
void xaccAccountTreeScrubCommodities(Account *acc)
The xaccAccountTreeScrubCommodities will scrub the currency/commodity of all accounts & transactions ...
Definition Scrub.cpp:1303
void xaccTransScrubOrphans(Transaction *trans)
The xaccTransScrubOrphans() method scrubs only the splits in the given transaction.
Definition Scrub.cpp:179
gboolean gnc_get_ongoing_scrub(void)
The gnc_get_ongoing_scrub () method returns TRUE if a scrub operation is ongoing.
Definition Scrub.cpp:87
void gnc_set_abort_scrub(gboolean abort)
The gnc_set_abort_scrub () method causes a currently running scrub operation to stop,...
Definition Scrub.cpp:75
void xaccSplitScrub(Split *split)
The xaccSplitScrub method ensures that if this split has the same commodity and currency,...
Definition Scrub.cpp:424
void xaccAccountScrubOrphans(Account *acc, QofPercentageFunc percentagefunc)
The xaccAccountScrubOrphans() method performs this scrub only for the indicated account,...
Definition Scrub.cpp:167
void xaccAccountScrubKvp(Account *account)
Removes empty "notes", "placeholder", and "hbci" KVP slots from Accounts.
Definition Scrub.cpp:1384
void xaccTransScrubCurrency(Transaction *trans)
The xaccTransScrubCurrency method fixes transactions without a common_currency by looking for the mos...
Definition Scrub.cpp:1121
void xaccAccountScrubColorNotSet(QofBook *book)
Remove color slots that have a "Not Set" value, since 2.4.0, fixed in 3.4 This should only be run onc...
Definition Scrub.cpp:1415
void xaccTransScrubSplits(Transaction *trans)
The xacc*ScrubSplits() calls xaccSplitScrub() on each split in the respective structure: transaction,...
Definition Scrub.cpp:395
void xaccAccountTreeScrubQuoteSources(Account *root, gnc_commodity_table *table)
This routine will migrate the information about price quote sources from the account data structures ...
Definition Scrub.cpp:1361
void xaccAccountScrubCommodity(Account *account)
The xaccAccountScrubCommodity method fixed accounts without a commodity by using the old account curr...
Definition Scrub.cpp:1238
void xaccTransScrubPostedDate(Transaction *trans)
Changes Transaction date_posted timestamps from 00:00 local to 11:00 UTC.
Definition Scrub.cpp:1543
void xaccAccountTreeScrubOrphans(Account *acc, QofPercentageFunc percentagefunc)
The xaccAccountTreeScrubOrphans() method performs this scrub for the indicated account and its childr...
Definition Scrub.cpp:173
void xaccTransScrubImbalance(Transaction *trans, Account *root, Account *account)
Correct transaction imbalances.
Definition Scrub.cpp:845
Transaction * xaccSplitGetParent(const Split *split)
Returns the parent transaction of the split.
gboolean xaccSplitDestroy(Split *split)
Destructor.
Definition Split.cpp:1506
void xaccSplitSetValue(Split *split, gnc_numeric val)
The xaccSplitSetValue() method sets the value of this split in the transaction's commodity.
Split * xaccMallocSplit(QofBook *book)
Constructor.
void xaccSplitSetAmount(Split *split, gnc_numeric amt)
The xaccSplitSetAmount() method sets the amount in the account's commodity that the split should have...
const char * xaccSplitGetAction(const Split *split)
Returns the action string.
gnc_numeric xaccSplitGetValue(const Split *split)
Returns the value of this split in the transaction's commodity.
Account * xaccSplitGetAccount(const Split *split)
Returns the account of this split, which was set through xaccAccountInsertSplit().
const char * xaccSplitGetMemo(const Split *split)
Returns the memo string.
gnc_numeric xaccSplitGetAmount(const Split *split)
Returns the amount of the split in the account's commodity.
STRUCTS.
QofBook reference.
Definition qofbook-p.hpp:47