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