Line data Source code
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 :
62 : static QofLogModule log_module = G_LOG_DOMAIN;
63 : static gboolean abort_now = FALSE;
64 : static gint scrub_depth = 0;
65 :
66 :
67 : static Account* xaccScrubUtilityGetOrMakeAccount (Account *root,
68 : gnc_commodity* currency,
69 : const char* accname,
70 : GNCAccountType acctype,
71 : gboolean placeholder,
72 : gboolean checkname);
73 :
74 : void
75 0 : gnc_set_abort_scrub (gboolean abort)
76 : {
77 0 : abort_now = abort;
78 0 : }
79 :
80 : gboolean
81 0 : gnc_get_abort_scrub (void)
82 : {
83 0 : return abort_now;
84 : }
85 :
86 : gboolean
87 0 : gnc_get_ongoing_scrub (void)
88 : {
89 0 : return scrub_depth > 0;
90 : }
91 :
92 : /* ================================================================ */
93 :
94 : using TransSet = std::unordered_set<Transaction*>;
95 :
96 : static TransSet
97 0 : get_all_transactions (Account *account, bool descendants)
98 : {
99 0 : TransSet set;
100 0 : auto add_transactions = [&set](auto a)
101 0 : { gnc_account_foreach_split (a, [&set](auto s){ set.insert (xaccSplitGetParent (s)); }); };
102 0 : add_transactions (account);
103 0 : if (descendants)
104 0 : gnc_account_foreach_descendant (account, add_transactions);
105 0 : return set;
106 0 : }
107 :
108 : /* ================================================================ */
109 :
110 : static void
111 1626 : TransScrubOrphansFast (Transaction *trans, Account *root)
112 : {
113 1626 : g_return_if_fail (trans && trans->common_currency && root);
114 :
115 4910 : for (GList *node = trans->splits; node; node = node->next)
116 : {
117 3300 : Split *split = GNC_SPLIT(node->data);
118 3300 : if (abort_now) break;
119 :
120 3300 : if (split->acc) continue;
121 :
122 61 : DEBUG ("Found an orphan\n");
123 :
124 : gchar *accname = g_strconcat
125 61 : (_("Orphan"), "-", gnc_commodity_get_mnemonic (trans->common_currency),
126 : nullptr);
127 :
128 : Account *orph = xaccScrubUtilityGetOrMakeAccount
129 61 : (root, trans->common_currency, accname, ACCT_TYPE_BANK, false, true);
130 :
131 61 : g_free (accname);
132 61 : if (!orph) continue;
133 :
134 61 : xaccSplitSetAccount(split, orph);
135 : }
136 : }
137 :
138 : static void
139 0 : AccountScrubOrphans (Account *acc, bool descendants, QofPercentageFunc percentagefunc)
140 : {
141 0 : if (!acc) return;
142 0 : scrub_depth++;
143 :
144 0 : auto transactions = get_all_transactions (acc, descendants);
145 0 : auto total_trans = transactions.size();
146 0 : const char *message = _("Looking for orphans in transaction: %u of %zu");
147 0 : guint current_trans = 0;
148 :
149 0 : for (auto trans : transactions)
150 : {
151 0 : if (current_trans % 10 == 0)
152 : {
153 0 : char *progress_msg = g_strdup_printf (message, current_trans, total_trans);
154 0 : (percentagefunc)(progress_msg, (100 * current_trans) / total_trans);
155 0 : g_free (progress_msg);
156 0 : if (abort_now) break;
157 : }
158 :
159 0 : TransScrubOrphansFast (trans, gnc_account_get_root (acc));
160 0 : current_trans++;
161 : }
162 0 : (percentagefunc)(nullptr, -1.0);
163 0 : scrub_depth--;
164 0 : }
165 :
166 : void
167 0 : xaccAccountScrubOrphans (Account *acc, QofPercentageFunc percentagefunc)
168 : {
169 0 : AccountScrubOrphans (acc, false, percentagefunc);
170 0 : }
171 :
172 : void
173 0 : xaccAccountTreeScrubOrphans (Account *acc, QofPercentageFunc percentagefunc)
174 : {
175 0 : AccountScrubOrphans (acc, true, percentagefunc);
176 0 : }
177 :
178 : void
179 1626 : xaccTransScrubOrphans (Transaction *trans)
180 : {
181 : SplitList *node;
182 1626 : QofBook *book = nullptr;
183 1626 : Account *root = nullptr;
184 :
185 1626 : if (!trans) return;
186 :
187 1698 : for (node = trans->splits; node; node = node->next)
188 : {
189 1632 : Split *split = GNC_SPLIT(node->data);
190 1632 : if (abort_now) break;
191 :
192 1632 : if (split->acc)
193 : {
194 1560 : TransScrubOrphansFast (trans, gnc_account_get_root(split->acc));
195 1560 : 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 66 : PINFO ("Free Floating Transaction!");
205 66 : book = xaccTransGetBook (trans);
206 66 : root = gnc_book_get_root_account (book);
207 66 : TransScrubOrphansFast (trans, root);
208 : }
209 :
210 : /* ================================================================ */
211 :
212 : void
213 22 : xaccAccountTreeScrubSplits (Account *account)
214 : {
215 22 : if (!account) return;
216 :
217 22 : xaccAccountScrubSplits (account);
218 22 : gnc_account_foreach_descendant(account,
219 : (AccountCb)xaccAccountScrubSplits, nullptr);
220 : }
221 :
222 : void
223 456 : xaccAccountScrubSplits (Account *account)
224 : {
225 456 : scrub_depth++;
226 2522 : for (auto s : xaccAccountGetSplits (account))
227 : {
228 1610 : if (abort_now) break;
229 1610 : xaccSplitScrub (s);
230 : }
231 456 : scrub_depth--;
232 456 : }
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. */
236 : static bool
237 8471 : split_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 8471 : if (!split) return false;
246 8471 : ENTER ("(split=%p)", split);
247 :
248 8471 : trans = xaccSplitGetParent (split);
249 8471 : if (!trans)
250 : {
251 8 : LEAVE("no trans");
252 8 : return false;
253 : }
254 :
255 8463 : 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 8463 : if (!account)
261 : {
262 146 : if (dry_run)
263 73 : return true;
264 : else
265 73 : xaccTransScrubOrphans (trans);
266 73 : 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 8390 : if (!account)
274 : {
275 16 : PINFO ("Free Floating Transaction!");
276 16 : LEAVE ("no account");
277 16 : return false;
278 : }
279 :
280 : /* Split amounts and values should be valid numbers */
281 8374 : value = xaccSplitGetValue (split);
282 8374 : if (gnc_numeric_check (value))
283 : {
284 0 : value = gnc_numeric_zero();
285 0 : if (dry_run)
286 0 : return true;
287 : else
288 0 : xaccSplitSetValue (split, value);
289 : }
290 :
291 8374 : amount = xaccSplitGetAmount (split);
292 8374 : if (gnc_numeric_check (amount))
293 : {
294 0 : amount = gnc_numeric_zero();
295 0 : if (dry_run)
296 0 : return true;
297 : else
298 0 : xaccSplitSetAmount (split, amount);
299 : }
300 :
301 8374 : currency = xaccTransGetCurrency (trans);
302 :
303 : /* If the account doesn't have a commodity,
304 : * we should attempt to fix that first.
305 : */
306 8374 : acc_commodity = xaccAccountGetCommodity(account);
307 8374 : if (!acc_commodity)
308 : {
309 1362 : if (dry_run)
310 454 : return true;
311 : else
312 908 : xaccAccountScrubCommodity (account);
313 : }
314 7920 : if (!acc_commodity || !gnc_commodity_equiv(acc_commodity, currency))
315 : {
316 2796 : LEAVE ("(split=%p) inequiv currency", split);
317 2796 : return false;
318 : }
319 :
320 5124 : scu = MIN (xaccAccountGetCommoditySCU (account),
321 : gnc_commodity_get_fraction (currency));
322 :
323 5124 : if (gnc_numeric_same (amount, value, scu, GNC_HOW_RND_ROUND_HALF_UP))
324 : {
325 5057 : LEAVE("(split=%p) different values", split);
326 5057 : return false;
327 : }
328 :
329 67 : if (dry_run)
330 30 : 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 37 : PINFO ("Adjusted split with mismatched values, desc=\"%s\" memo=\"%s\""
337 : " old amount %s %s, new amount %s",
338 : trans->description, split->memo,
339 : gnc_num_dbg_to_string (xaccSplitGetAmount(split)),
340 : gnc_commodity_get_mnemonic (currency),
341 : gnc_num_dbg_to_string (xaccSplitGetValue(split)));
342 :
343 37 : xaccTransBeginEdit (trans);
344 37 : xaccSplitSetAmount (split, value);
345 37 : xaccTransCommitEdit (trans);
346 37 : LEAVE ("(split=%p)", split);
347 37 : return true;
348 : }
349 :
350 : /* ================================================================ */
351 :
352 :
353 : static void
354 0 : AccountScrubImbalance (Account *acc, bool descendants,
355 : QofPercentageFunc percentagefunc)
356 : {
357 0 : const char *message = _("Looking for imbalances in transaction date %s: %u of %zu");
358 :
359 0 : if (!acc) return;
360 :
361 0 : QofBook *book = qof_session_get_book (gnc_get_current_session ());
362 0 : Account *root = gnc_book_get_root_account (book);
363 0 : auto transactions = get_all_transactions (acc, descendants);
364 0 : auto count = transactions.size();
365 0 : auto curr_trans = 0;
366 :
367 0 : scrub_depth++;
368 0 : for (auto trans : transactions)
369 : {
370 0 : if (abort_now) break;
371 :
372 0 : PINFO("Start processing transaction %d of %zu", curr_trans + 1, count);
373 :
374 0 : if (curr_trans % 10 == 0)
375 : {
376 0 : char *date = qof_print_date (xaccTransGetDate (trans));
377 0 : char *progress_msg = g_strdup_printf (message, date, curr_trans, count);
378 0 : (percentagefunc)(progress_msg, (100 * curr_trans) / count);
379 0 : g_free (progress_msg);
380 0 : g_free (date);
381 : }
382 :
383 0 : TransScrubOrphansFast (trans, root);
384 0 : xaccTransScrubCurrency(trans);
385 0 : xaccTransScrubImbalance (trans, root, nullptr);
386 :
387 0 : PINFO("Finished processing transaction %d of %zu", curr_trans + 1, count);
388 0 : curr_trans++;
389 : }
390 0 : (percentagefunc)(nullptr, -1.0);
391 0 : scrub_depth--;
392 0 : }
393 :
394 : void
395 3060 : xaccTransScrubSplits (Transaction *trans)
396 : {
397 3060 : if (!trans) return;
398 :
399 3060 : gnc_commodity *currency = xaccTransGetCurrency (trans);
400 3060 : if (!currency)
401 166 : PERR ("Transaction doesn't have a currency!");
402 :
403 3060 : bool must_scrub = false;
404 :
405 14504 : for (GList *n = xaccTransGetSplitList (trans); !must_scrub && n; n = g_list_next (n))
406 5722 : if (split_scrub_or_dry_run (GNC_SPLIT(n->data), true))
407 557 : must_scrub = true;
408 :
409 3060 : if (!must_scrub)
410 2503 : return;
411 :
412 557 : xaccTransBeginEdit(trans);
413 : /* The split scrub expects the transaction to have a currency! */
414 :
415 2737 : for (GList *n = xaccTransGetSplitList (trans); n; n = g_list_next (n))
416 1090 : xaccSplitScrub (GNC_SPLIT(n->data));
417 :
418 557 : xaccTransCommitEdit(trans);
419 : }
420 :
421 : /* ================================================================ */
422 :
423 : void
424 2749 : xaccSplitScrub (Split *split)
425 : {
426 2749 : split_scrub_or_dry_run (split, false);
427 2749 : }
428 :
429 : /* ================================================================ */
430 :
431 :
432 : void
433 0 : xaccAccountTreeScrubImbalance (Account *acc, QofPercentageFunc percentagefunc)
434 : {
435 0 : AccountScrubImbalance (acc, true, percentagefunc);
436 0 : }
437 :
438 : void
439 0 : xaccAccountScrubImbalance (Account *acc, QofPercentageFunc percentagefunc)
440 : {
441 0 : AccountScrubImbalance (acc, false, percentagefunc);
442 0 : }
443 :
444 : static Split *
445 46 : get_balance_split (Transaction *trans, Account *root, Account *account,
446 : gnc_commodity *commodity)
447 : {
448 : Split *balance_split;
449 : gchar *accname;
450 :
451 46 : if (!account ||
452 0 : !gnc_commodity_equiv (commodity, xaccAccountGetCommodity(account)))
453 : {
454 46 : if (!root)
455 : {
456 46 : root = gnc_book_get_root_account (xaccTransGetBook (trans));
457 46 : if (nullptr == root)
458 : {
459 : /* This can't occur, things should be in books */
460 0 : PERR ("Bad data corruption, no root account in book");
461 0 : return nullptr;
462 : }
463 : }
464 46 : accname = g_strconcat (_("Imbalance"), "-",
465 : gnc_commodity_get_mnemonic (commodity), nullptr);
466 46 : account = xaccScrubUtilityGetOrMakeAccount (root, commodity,
467 : accname, ACCT_TYPE_BANK,
468 : FALSE, TRUE);
469 46 : g_free (accname);
470 46 : if (!account)
471 : {
472 0 : PERR ("Can't get balancing account");
473 0 : return nullptr;
474 : }
475 : }
476 :
477 46 : balance_split = xaccTransFindSplitByAccount(trans, account);
478 :
479 : /* Put split into account before setting split value */
480 46 : if (!balance_split)
481 : {
482 37 : balance_split = xaccMallocSplit (qof_instance_get_book(trans));
483 :
484 37 : xaccTransBeginEdit (trans);
485 37 : xaccSplitSetParent(balance_split, trans);
486 37 : xaccSplitSetAccount(balance_split, account);
487 37 : xaccTransCommitEdit (trans);
488 : }
489 :
490 46 : return balance_split;
491 : }
492 :
493 : static gnc_commodity*
494 83 : find_root_currency(void)
495 : {
496 83 : QofSession *sess = gnc_get_current_session ();
497 83 : Account *root = gnc_book_get_root_account (qof_session_get_book (sess));
498 83 : 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 83 : if (!root_currency)
504 : {
505 83 : GList *children = gnc_account_get_children (root);
506 87 : for (GList *node = children; node && !root_currency;
507 2 : node = g_list_next (node))
508 : {
509 2 : Account *child = GNC_ACCOUNT (node->data);
510 2 : if (xaccAccountGetType (child) == ACCT_TYPE_INCOME)
511 0 : root_currency = xaccAccountGetCommodity (child);
512 : }
513 83 : g_list_free (children);
514 : }
515 83 : 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. */
520 : static Split *
521 0 : get_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 0 : Account* root = gnc_book_get_root_account (xaccTransGetBook (trans));
529 :
530 0 : trading_account = xaccScrubUtilityGetOrMakeAccount (root,
531 : nullptr,
532 0 : _("Trading"),
533 : ACCT_TYPE_TRADING,
534 : TRUE, FALSE);
535 0 : if (!trading_account)
536 : {
537 0 : PERR ("Can't get trading account");
538 0 : return nullptr;
539 : }
540 :
541 0 : ns_account = xaccScrubUtilityGetOrMakeAccount (trading_account,
542 : nullptr,
543 : gnc_commodity_get_namespace(commodity),
544 : ACCT_TYPE_TRADING,
545 : TRUE, TRUE);
546 0 : if (!ns_account)
547 : {
548 0 : PERR ("Can't get namespace account");
549 0 : return nullptr;
550 : }
551 :
552 0 : account = xaccScrubUtilityGetOrMakeAccount (ns_account, commodity,
553 : gnc_commodity_get_mnemonic(commodity),
554 : ACCT_TYPE_TRADING,
555 : FALSE, FALSE);
556 0 : if (!account)
557 : {
558 0 : PERR ("Can't get commodity account");
559 0 : return nullptr;
560 : }
561 :
562 :
563 0 : balance_split = xaccTransFindSplitByAccount(trans, account);
564 :
565 : /* Put split into account before setting split value */
566 0 : if (!balance_split)
567 : {
568 0 : balance_split = xaccMallocSplit (qof_instance_get_book(trans));
569 0 : xaccDisableDataScrubbing();
570 :
571 0 : xaccTransBeginEdit (trans);
572 0 : xaccSplitSetParent(balance_split, trans);
573 0 : xaccSplitSetAccount(balance_split, account);
574 0 : xaccTransCommitEdit (trans);
575 0 : xaccEnableDataScrubbing();
576 : }
577 :
578 0 : return balance_split;
579 : }
580 :
581 : static void
582 46 : add_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 46 : gnc_commodity *currency = xaccTransGetCurrency (trans);
589 :
590 46 : balance_split = get_balance_split(trans, root, account, currency);
591 46 : if (!balance_split)
592 : {
593 : /* Error already logged */
594 0 : LEAVE("");
595 0 : return;
596 : }
597 :
598 46 : 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 46 : new_value = gnc_numeric_sub (old_value, imbalance,
604 46 : gnc_commodity_get_fraction(currency),
605 : GNC_HOW_RND_ROUND_HALF_UP);
606 :
607 46 : if (gnc_numeric_zero_p (new_value))
608 : {
609 : const char *p;
610 5 : p = xaccSplitGetMemo (balance_split);
611 5 : if (!p || !*p)
612 : {
613 5 : p = xaccSplitGetAction (balance_split);
614 5 : if (!p || !*p)
615 : {
616 5 : xaccSplitDestroy (balance_split);
617 5 : return;
618 : }
619 : }
620 : }
621 :
622 41 : xaccTransBeginEdit (trans);
623 41 : xaccSplitSetValue (balance_split, new_value);
624 :
625 41 : account = xaccSplitGetAccount(balance_split);
626 41 : commodity = xaccAccountGetCommodity (account);
627 41 : if (gnc_commodity_equiv (currency, commodity))
628 : {
629 41 : xaccSplitSetAmount (balance_split, new_value);
630 : }
631 :
632 41 : xaccSplitScrub (balance_split);
633 41 : xaccTransCommitEdit (trans);
634 : }
635 :
636 : /* Balance a transaction without trading accounts. */
637 : static void
638 46 : gnc_transaction_balance_no_trading (Transaction *trans, Account *root,
639 : Account *account)
640 : {
641 46 : gnc_numeric imbalance = xaccTransGetImbalanceValue (trans);
642 :
643 : /* Make the value sum to zero */
644 46 : if (! gnc_numeric_zero_p (imbalance))
645 : {
646 46 : PINFO ("Value unbalanced transaction");
647 :
648 46 : add_balance_split (trans, imbalance, root, account);
649 : }
650 :
651 46 : }
652 :
653 : static gnc_numeric
654 0 : gnc_transaction_get_commodity_imbalance (Transaction *trans,
655 : gnc_commodity *commodity)
656 : {
657 : /* Find the value imbalance in this commodity */
658 0 : gnc_numeric val_imbalance = gnc_numeric_zero();
659 0 : GList *splits = nullptr;
660 0 : for (splits = trans->splits; splits; splits = splits->next)
661 : {
662 0 : Split *split = GNC_SPLIT(splits->data);
663 : gnc_commodity *split_commodity =
664 0 : xaccAccountGetCommodity(xaccSplitGetAccount(split));
665 0 : if (xaccTransStillHasSplit (trans, split) &&
666 0 : gnc_commodity_equal (commodity, split_commodity))
667 0 : val_imbalance = gnc_numeric_add (val_imbalance,
668 : xaccSplitGetValue (split),
669 : GNC_DENOM_AUTO,
670 : GNC_HOW_DENOM_EXACT);
671 : }
672 0 : return val_imbalance;
673 : }
674 :
675 : /* GFunc wrapper for xaccSplitDestroy */
676 : static void
677 0 : destroy_split (void* ptr)
678 : {
679 0 : Split *split = GNC_SPLIT (ptr);
680 0 : if (split)
681 0 : xaccSplitDestroy (split);
682 0 : }
683 :
684 : /* Balancing transactions with trading accounts works best when
685 : * starting with no trading splits.
686 : */
687 : static void
688 0 : xaccTransClearTradingSplits (Transaction *trans)
689 : {
690 0 : GList *trading_splits = nullptr;
691 :
692 0 : for (GList* node = trans->splits; node; node = node->next)
693 : {
694 0 : Split* split = GNC_SPLIT(node->data);
695 0 : Account* acc = nullptr;
696 0 : if (!split)
697 0 : continue;
698 0 : acc = xaccSplitGetAccount(split);
699 0 : if (acc && xaccAccountGetType(acc) == ACCT_TYPE_TRADING)
700 0 : trading_splits = g_list_prepend (trading_splits, node->data);
701 : }
702 :
703 0 : if (!trading_splits)
704 0 : return;
705 :
706 0 : xaccTransBeginEdit (trans);
707 : /* destroy_splits doesn't actually free the splits but this gets
708 : * the list itself freed.
709 : */
710 0 : g_list_free_full (trading_splits, destroy_split);
711 0 : xaccTransCommitEdit (trans);
712 : }
713 :
714 : static void
715 0 : gnc_transaction_balance_trading (Transaction *trans, Account *root)
716 : {
717 : MonetaryList *imbal_list;
718 : MonetaryList *imbalance_commod;
719 0 : Split *balance_split = nullptr;
720 :
721 : /* If the transaction is balanced, nothing more to do */
722 0 : imbal_list = xaccTransGetImbalance (trans);
723 0 : if (!imbal_list)
724 : {
725 0 : LEAVE("transaction is balanced");
726 0 : return;
727 : }
728 :
729 0 : PINFO ("Currency unbalanced transaction");
730 :
731 0 : for (imbalance_commod = imbal_list; imbalance_commod;
732 0 : imbalance_commod = imbalance_commod->next)
733 : {
734 0 : auto imbal_mon = static_cast<gnc_monetary*>(imbalance_commod->data);
735 : gnc_commodity *commodity;
736 : gnc_numeric old_amount, new_amount;
737 0 : const gnc_commodity *txn_curr = xaccTransGetCurrency (trans);
738 :
739 0 : commodity = gnc_monetary_commodity (*imbal_mon);
740 :
741 0 : balance_split = get_trading_split(trans, root, commodity);
742 0 : if (!balance_split)
743 : {
744 : /* Error already logged */
745 0 : gnc_monetary_list_free(imbal_list);
746 0 : LEAVE("");
747 0 : return;
748 : }
749 :
750 0 : xaccTransBeginEdit (trans);
751 :
752 0 : old_amount = xaccSplitGetAmount (balance_split);
753 0 : new_amount = gnc_numeric_sub (old_amount, gnc_monetary_value(*imbal_mon),
754 0 : gnc_commodity_get_fraction(commodity),
755 : GNC_HOW_RND_ROUND_HALF_UP);
756 :
757 0 : xaccSplitSetAmount (balance_split, new_amount);
758 :
759 0 : 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 0 : xaccSplitSetValue (balance_split, new_amount);
764 : }
765 : else
766 : {
767 0 : gnc_numeric val_imbalance = gnc_transaction_get_commodity_imbalance (trans, commodity);
768 :
769 0 : gnc_numeric old_value = xaccSplitGetValue (balance_split);
770 0 : gnc_numeric new_value = gnc_numeric_sub (old_value, val_imbalance,
771 0 : gnc_commodity_get_fraction(txn_curr),
772 : GNC_HOW_RND_ROUND_HALF_UP);
773 :
774 0 : xaccSplitSetValue (balance_split, new_value);
775 : }
776 :
777 0 : xaccSplitScrub (balance_split);
778 0 : xaccTransCommitEdit (trans);
779 : }
780 :
781 0 : gnc_monetary_list_free(imbal_list);
782 : }
783 :
784 : /** Balance the transaction by adding more trading splits. This shouldn't
785 : * ordinarily be necessary.
786 : * @param trans the transaction to balance
787 : * @param root the root account
788 : */
789 : static void
790 0 : gnc_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 0 : GList *splits_dup = g_list_copy(trans->splits), *splits = nullptr;
794 0 : const gnc_commodity *txn_curr = xaccTransGetCurrency (trans);
795 0 : for (splits = splits_dup; splits; splits = splits->next)
796 : {
797 0 : Split *split = GNC_SPLIT(splits->data);
798 0 : if (! xaccTransStillHasSplit(trans, split)) continue;
799 0 : if (!gnc_numeric_zero_p(xaccSplitGetValue(split)) &&
800 0 : gnc_numeric_zero_p(xaccSplitGetAmount(split)))
801 : {
802 : gnc_commodity *commodity;
803 : gnc_numeric old_value, new_value;
804 : Split *balance_split;
805 :
806 0 : commodity = xaccAccountGetCommodity(xaccSplitGetAccount(split));
807 0 : if (!commodity)
808 : {
809 0 : PERR("Split has no commodity");
810 0 : continue;
811 : }
812 0 : balance_split = get_trading_split(trans, root, commodity);
813 0 : if (!balance_split)
814 : {
815 : /* Error already logged */
816 0 : LEAVE("");
817 0 : return;
818 : }
819 0 : xaccTransBeginEdit (trans);
820 :
821 0 : old_value = xaccSplitGetValue (balance_split);
822 0 : new_value = gnc_numeric_sub (old_value, xaccSplitGetValue(split),
823 0 : gnc_commodity_get_fraction(txn_curr),
824 : GNC_HOW_RND_ROUND_HALF_UP);
825 0 : 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 0 : xaccSplitScrub (balance_split);
831 0 : xaccTransCommitEdit (trans);
832 : }
833 : }
834 :
835 0 : g_list_free(splits_dup);
836 : }
837 :
838 : /** Correct transaction imbalances.
839 : * @param trans The Transaction
840 : * @param root The (hidden) root account, for the book default currency.
841 : * @param account The account whose currency in which to balance.
842 : */
843 :
844 : void
845 3060 : xaccTransScrubImbalance (Transaction *trans, Account *root,
846 : Account *account)
847 : {
848 : gnc_numeric imbalance;
849 :
850 6120 : if (!trans) return;
851 :
852 3060 : ENTER ("()");
853 :
854 : /* Must look for orphan splits even if there is no imbalance. */
855 3060 : xaccTransScrubSplits (trans);
856 :
857 : /* Return immediately if things are balanced. */
858 3060 : if (xaccTransIsBalanced (trans))
859 : {
860 3014 : LEAVE ("transaction is balanced");
861 3014 : return;
862 : }
863 :
864 46 : if (! xaccTransUseTradingAccounts (trans))
865 : {
866 46 : gnc_transaction_balance_no_trading (trans, root, account);
867 46 : LEAVE ("transaction balanced, no managed trading accounts");
868 46 : return;
869 : }
870 :
871 0 : xaccTransClearTradingSplits (trans);
872 0 : imbalance = xaccTransGetImbalanceValue (trans);
873 0 : if (! gnc_numeric_zero_p (imbalance))
874 : {
875 0 : PINFO ("Value unbalanced transaction");
876 :
877 0 : add_balance_split (trans, imbalance, root, account);
878 : }
879 :
880 0 : gnc_transaction_balance_trading (trans, root);
881 0 : if (gnc_numeric_zero_p(xaccTransGetImbalanceValue(trans)))
882 : {
883 0 : LEAVE ("()");
884 0 : 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 0 : gnc_transaction_balance_trading_more_splits (trans, root);
892 0 : if (!gnc_numeric_zero_p(xaccTransGetImbalanceValue(trans)))
893 0 : 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 :
904 : static gnc_commodity *
905 0 : FindCommonExclSCurrency (SplitList *splits,
906 : gnc_commodity * ra, gnc_commodity * rb,
907 : Split *excl_split)
908 : {
909 : GList *node;
910 :
911 0 : if (!splits) return nullptr;
912 :
913 0 : for (node = splits; node; node = node->next)
914 : {
915 0 : Split *s = GNC_SPLIT(node->data);
916 : gnc_commodity * sa, * sb;
917 :
918 0 : if (s == excl_split) continue;
919 :
920 0 : g_return_val_if_fail (s->acc, nullptr);
921 :
922 0 : sa = DxaccAccountGetCurrency (s->acc);
923 0 : sb = xaccAccountGetCommodity (s->acc);
924 :
925 0 : if (ra && rb)
926 : {
927 0 : int aa = !gnc_commodity_equiv(ra, sa);
928 0 : int ab = !gnc_commodity_equiv(ra, sb);
929 0 : int ba = !gnc_commodity_equiv(rb, sa);
930 0 : int bb = !gnc_commodity_equiv(rb, sb);
931 :
932 0 : if ( (!aa) && bb) rb = nullptr;
933 0 : else if ( (!ab) && ba) rb = nullptr;
934 0 : else if ( (!ba) && ab) ra = nullptr;
935 0 : else if ( (!bb) && aa) ra = nullptr;
936 0 : else if ( aa && bb && ab && ba )
937 : {
938 0 : ra = nullptr;
939 0 : rb = nullptr;
940 : }
941 :
942 0 : if (!ra)
943 : {
944 0 : ra = rb;
945 0 : rb = nullptr;
946 : }
947 0 : }
948 0 : else if (ra && !rb)
949 : {
950 0 : int aa = !gnc_commodity_equiv(ra, sa);
951 0 : int ab = !gnc_commodity_equiv(ra, sb);
952 0 : if ( aa && ab ) ra = nullptr;
953 0 : }
954 0 : else if (!ra && rb)
955 : {
956 0 : int aa = !gnc_commodity_equiv(rb, sa);
957 0 : int ab = !gnc_commodity_equiv(rb, sb);
958 0 : ra = ( aa && ab ) ? nullptr : rb;
959 : }
960 :
961 0 : if ((!ra) && (!rb)) return nullptr;
962 : }
963 :
964 0 : 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 : */
971 : static gnc_commodity *
972 0 : FindCommonCurrency (GList *splits, gnc_commodity * ra, gnc_commodity * rb)
973 : {
974 0 : return FindCommonExclSCurrency(splits, ra, rb, nullptr);
975 : }
976 :
977 : static gnc_commodity *
978 0 : xaccTransFindOldCommonCurrency (Transaction *trans, QofBook *book)
979 : {
980 : gnc_commodity *ra, *rb, *retval;
981 : Split *split;
982 :
983 0 : if (!trans) return nullptr;
984 :
985 0 : if (trans->splits == nullptr) return nullptr;
986 :
987 0 : g_return_val_if_fail (book, nullptr);
988 :
989 0 : split = GNC_SPLIT(trans->splits->data);
990 :
991 0 : if (!split || nullptr == split->acc) return nullptr;
992 :
993 0 : ra = DxaccAccountGetCurrency (split->acc);
994 0 : rb = xaccAccountGetCommodity (split->acc);
995 :
996 0 : retval = FindCommonCurrency (trans->splits, ra, rb);
997 :
998 0 : if (retval && !gnc_commodity_is_currency(retval))
999 0 : retval = nullptr;
1000 :
1001 0 : 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 : */
1008 : typedef struct
1009 : {
1010 : gnc_commodity *commodity;
1011 : unsigned int count;
1012 : } CommodityCount;
1013 :
1014 : static gint
1015 202 : commodity_equal (gconstpointer a, gconstpointer b)
1016 : {
1017 202 : CommodityCount *cc = (CommodityCount*)a;
1018 202 : gnc_commodity *com = (gnc_commodity*)b;
1019 404 : if ( cc == nullptr || cc->commodity == nullptr ||
1020 202 : !GNC_IS_COMMODITY( cc->commodity ) ) return -1;
1021 202 : if ( com == nullptr || !GNC_IS_COMMODITY( com ) ) return 1;
1022 202 : if ( gnc_commodity_equal(cc->commodity, com) )
1023 202 : return 0;
1024 0 : return 1;
1025 : }
1026 :
1027 : static gint
1028 0 : commodity_compare( gconstpointer a, gconstpointer b)
1029 : {
1030 0 : CommodityCount *ca = (CommodityCount*)a, *cb = (CommodityCount*)b;
1031 0 : if (ca == nullptr || ca->commodity == nullptr ||
1032 0 : !GNC_IS_COMMODITY( ca->commodity ) )
1033 : {
1034 0 : if (cb == nullptr || cb->commodity == nullptr ||
1035 0 : !GNC_IS_COMMODITY( cb->commodity ) )
1036 0 : return 0;
1037 0 : return -1;
1038 : }
1039 0 : if (cb == nullptr || cb->commodity == nullptr ||
1040 0 : !GNC_IS_COMMODITY( cb->commodity ) )
1041 0 : return 1;
1042 0 : if (ca->count == cb->count)
1043 0 : return 0;
1044 0 : 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 :
1055 : static gnc_commodity *
1056 198 : xaccTransFindCommonCurrency (Transaction *trans, QofBook *book)
1057 : {
1058 : gnc_commodity *com_scratch;
1059 198 : GList *node = nullptr;
1060 198 : GSList *comlist = nullptr, *found = nullptr;
1061 :
1062 198 : if (!trans) return nullptr;
1063 :
1064 198 : if (trans->splits == nullptr) return nullptr;
1065 :
1066 198 : 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 601 : for (node = trans->splits; node; node = node->next)
1073 : {
1074 403 : Split *s = GNC_SPLIT(node->data);
1075 : unsigned int curr_weight;
1076 :
1077 403 : if (s == nullptr || s->acc == nullptr) continue;
1078 403 : if (xaccAccountGetType(s->acc) == ACCT_TYPE_TRADING) continue;
1079 403 : com_scratch = xaccAccountGetCommodity(s->acc);
1080 403 : if (com_scratch && gnc_commodity_is_currency(com_scratch))
1081 : {
1082 71 : curr_weight = 3;
1083 : }
1084 : else
1085 : {
1086 332 : com_scratch = gnc_account_get_currency_or_parent(s->acc);
1087 332 : if (com_scratch == nullptr) continue;
1088 329 : curr_weight = 1;
1089 : }
1090 400 : if ( comlist )
1091 : {
1092 202 : found = g_slist_find_custom(comlist, com_scratch, commodity_equal);
1093 : }
1094 400 : if (comlist == nullptr || found == nullptr)
1095 : {
1096 198 : CommodityCount *count = g_slice_new0(CommodityCount);
1097 198 : count->commodity = com_scratch;
1098 198 : count->count = curr_weight;
1099 198 : comlist = g_slist_append(comlist, count);
1100 198 : }
1101 : else
1102 : {
1103 202 : CommodityCount *count = (CommodityCount*)(found->data);
1104 202 : count->count += curr_weight;
1105 : }
1106 : }
1107 198 : found = g_slist_sort( comlist, commodity_compare);
1108 :
1109 198 : if ( found && found->data && (((CommodityCount*)(found->data))->commodity != nullptr))
1110 : {
1111 198 : 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 0 : return xaccTransFindOldCommonCurrency( trans, book );
1116 : }
1117 :
1118 : /* ================================================================ */
1119 :
1120 : void
1121 1553 : xaccTransScrubCurrency (Transaction *trans)
1122 : {
1123 : SplitList *node;
1124 : gnc_commodity *currency;
1125 :
1126 1553 : 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 1553 : xaccTransScrubOrphans (trans);
1133 :
1134 1553 : currency = xaccTransGetCurrency (trans);
1135 1553 : if (currency && gnc_commodity_is_currency(currency)) return;
1136 :
1137 198 : currency = xaccTransFindCommonCurrency (trans, qof_instance_get_book(trans));
1138 198 : if (currency)
1139 : {
1140 198 : xaccTransBeginEdit (trans);
1141 198 : xaccTransSetCurrency (trans, currency);
1142 198 : xaccTransCommitEdit (trans);
1143 : }
1144 : else
1145 : {
1146 0 : if (nullptr == trans->splits)
1147 : {
1148 0 : 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 0 : guid_to_string_buff(xaccTransGetGUID(trans), guid_str);
1155 0 : PWARN ("no common transaction currency found for trans=\"%s\" (%s);",
1156 : trans->description, guid_str);
1157 :
1158 0 : for (node = trans->splits; node; node = node->next)
1159 : {
1160 0 : Split *split = GNC_SPLIT(node->data);
1161 0 : if (nullptr == split->acc)
1162 : {
1163 0 : PWARN (" split=\"%s\" is not in any account!", split->memo);
1164 : }
1165 : else
1166 : {
1167 0 : gnc_commodity *currency = xaccAccountGetCommodity(split->acc);
1168 0 : PWARN ("setting to split=\"%s\" account=\"%s\" commodity=\"%s\"",
1169 : split->memo, xaccAccountGetName(split->acc),
1170 : gnc_commodity_get_mnemonic(currency));
1171 :
1172 0 : xaccTransBeginEdit (trans);
1173 0 : xaccTransSetCurrency (trans, currency);
1174 0 : xaccTransCommitEdit (trans);
1175 0 : return;
1176 : }
1177 : }
1178 : }
1179 0 : return;
1180 : }
1181 :
1182 601 : for (node = trans->splits; node; node = node->next)
1183 : {
1184 403 : Split *sp = GNC_SPLIT(node->data);
1185 :
1186 403 : if (!gnc_numeric_equal(xaccSplitGetAmount (sp),
1187 : xaccSplitGetValue (sp)))
1188 : {
1189 : gnc_commodity *acc_currency;
1190 :
1191 299 : acc_currency = sp->acc ? xaccAccountGetCommodity(sp->acc) : nullptr;
1192 299 : 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 0 : PWARN ("Adjusted split with mismatched values, desc=\"%s\" memo=\"%s\""
1212 : " old amount %s %s, new amount %s",
1213 : trans->description, sp->memo,
1214 : gnc_num_dbg_to_string (xaccSplitGetAmount(sp)),
1215 : gnc_commodity_get_mnemonic (currency),
1216 : gnc_num_dbg_to_string (xaccSplitGetValue(sp)));
1217 0 : xaccTransBeginEdit (trans);
1218 0 : xaccSplitSetAmount (sp, xaccSplitGetValue(sp));
1219 0 : 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 :
1237 : void
1238 2543 : xaccAccountScrubCommodity (Account *account)
1239 : {
1240 : gnc_commodity *commodity;
1241 :
1242 2543 : if (!account) return;
1243 2543 : if (xaccAccountGetType(account) == ACCT_TYPE_ROOT) return;
1244 :
1245 2466 : commodity = xaccAccountGetCommodity (account);
1246 2466 : if (commodity) return;
1247 :
1248 : /* Use the 'obsolete' routines to try to figure out what the
1249 : * account commodity should have been. */
1250 908 : commodity = xaccAccountGetCommodity (account);
1251 908 : if (commodity)
1252 : {
1253 0 : xaccAccountSetCommodity (account, commodity);
1254 0 : return;
1255 : }
1256 :
1257 908 : commodity = DxaccAccountGetCurrency (account);
1258 908 : if (commodity)
1259 : {
1260 0 : xaccAccountSetCommodity (account, commodity);
1261 0 : return;
1262 : }
1263 :
1264 908 : PERR ("Account \"%s\" does not have a commodity!",
1265 : xaccAccountGetName(account));
1266 : }
1267 :
1268 : /* ================================================================ */
1269 :
1270 : /* EFFECTIVE FRIEND FUNCTION declared in qofinstance-p.h */
1271 : extern void qof_instance_set_dirty (QofInstance*);
1272 :
1273 : static void
1274 456 : xaccAccountDeleteOldData (Account *account)
1275 : {
1276 456 : if (!account) return;
1277 456 : xaccAccountBeginEdit (account);
1278 456 : qof_instance_set_kvp (QOF_INSTANCE (account), nullptr, 1, "old-currency");
1279 456 : qof_instance_set_kvp (QOF_INSTANCE (account), nullptr, 1, "old-security");
1280 456 : qof_instance_set_kvp (QOF_INSTANCE (account), nullptr, 1, "old-currency-scu");
1281 456 : qof_instance_set_kvp (QOF_INSTANCE (account), nullptr, 1, "old-security-scu");
1282 456 : qof_instance_set_dirty (QOF_INSTANCE (account));
1283 456 : xaccAccountCommitEdit (account);
1284 : }
1285 :
1286 : static int
1287 774 : scrub_trans_currency_helper (Transaction *t, gpointer data)
1288 : {
1289 774 : xaccTransScrubCurrency (t);
1290 774 : return 0;
1291 : }
1292 :
1293 : static void
1294 456 : scrub_account_commodity_helper (Account *account, gpointer data)
1295 : {
1296 456 : scrub_depth++;
1297 456 : xaccAccountScrubCommodity (account);
1298 456 : xaccAccountDeleteOldData (account);
1299 456 : scrub_depth--;
1300 456 : }
1301 :
1302 : void
1303 22 : xaccAccountTreeScrubCommodities (Account *acc)
1304 : {
1305 22 : if (!acc) return;
1306 22 : scrub_depth++;
1307 22 : xaccAccountTreeForEachTransaction (acc, scrub_trans_currency_helper, nullptr);
1308 :
1309 22 : scrub_account_commodity_helper (acc, nullptr);
1310 22 : gnc_account_foreach_descendant (acc, scrub_account_commodity_helper, nullptr);
1311 22 : scrub_depth--;
1312 : }
1313 :
1314 : /* ================================================================ */
1315 :
1316 : static gboolean
1317 5080 : check_quote_source (gnc_commodity *com, gpointer data)
1318 : {
1319 5080 : gboolean *commodity_has_quote_src = (gboolean *)data;
1320 5080 : if (com && !gnc_commodity_is_iso(com))
1321 86 : *commodity_has_quote_src |= gnc_commodity_get_quote_flag(com);
1322 5080 : return TRUE;
1323 : }
1324 :
1325 : static void
1326 456 : move_quote_source (Account *account, gpointer data)
1327 : {
1328 : gnc_commodity *com;
1329 : gnc_quote_source *quote_source;
1330 456 : gboolean new_style = GPOINTER_TO_INT(data);
1331 : const char *source, *tz;
1332 :
1333 456 : com = xaccAccountGetCommodity(account);
1334 456 : if (!com)
1335 19 : return;
1336 :
1337 437 : if (!new_style)
1338 : {
1339 424 : source = dxaccAccountGetPriceSrc(account);
1340 424 : if (!source || !*source)
1341 424 : return;
1342 0 : tz = dxaccAccountGetQuoteTZ(account);
1343 :
1344 0 : PINFO("to %8s from %s", gnc_commodity_get_mnemonic(com),
1345 : xaccAccountGetName(account));
1346 0 : gnc_commodity_set_quote_flag(com, TRUE);
1347 0 : quote_source = gnc_quote_source_lookup_by_internal(source);
1348 0 : if (!quote_source)
1349 0 : quote_source = gnc_quote_source_add_new(source, FALSE);
1350 0 : gnc_commodity_set_quote_source(com, quote_source);
1351 0 : gnc_commodity_set_quote_tz(com, tz);
1352 : }
1353 :
1354 13 : dxaccAccountSetPriceSrc(account, nullptr);
1355 13 : dxaccAccountSetQuoteTZ(account, nullptr);
1356 13 : return;
1357 : }
1358 :
1359 :
1360 : void
1361 22 : xaccAccountTreeScrubQuoteSources (Account *root, gnc_commodity_table *table)
1362 : {
1363 22 : gboolean new_style = FALSE;
1364 22 : ENTER(" ");
1365 :
1366 22 : if (!root || !table)
1367 : {
1368 0 : LEAVE("Oops");
1369 0 : return;
1370 : }
1371 22 : scrub_depth++;
1372 22 : gnc_commodity_table_foreach_commodity (table, check_quote_source, &new_style);
1373 :
1374 22 : move_quote_source(root, GINT_TO_POINTER(new_style));
1375 22 : gnc_account_foreach_descendant (root, move_quote_source,
1376 22 : GINT_TO_POINTER(new_style));
1377 22 : LEAVE("Migration done");
1378 22 : scrub_depth--;
1379 : }
1380 :
1381 : /* ================================================================ */
1382 :
1383 : void
1384 455 : xaccAccountScrubKvp (Account *account)
1385 : {
1386 455 : GValue v = G_VALUE_INIT;
1387 : gchar *str2;
1388 :
1389 455 : if (!account) return;
1390 455 : scrub_depth++;
1391 :
1392 455 : qof_instance_get_kvp (QOF_INSTANCE (account), &v, 1, "notes");
1393 455 : if (G_VALUE_HOLDS_STRING (&v))
1394 : {
1395 1 : str2 = g_strstrip(g_value_dup_string(&v));
1396 1 : if (strlen(str2) == 0)
1397 0 : qof_instance_slot_delete (QOF_INSTANCE (account), "notes");
1398 1 : g_free(str2);
1399 : }
1400 :
1401 455 : qof_instance_get_kvp (QOF_INSTANCE (account), &v, 1, "placeholder");
1402 455 : if ((G_VALUE_HOLDS_STRING (&v) &&
1403 910 : strcmp(g_value_get_string (&v), "false") == 0) ||
1404 455 : (G_VALUE_HOLDS_BOOLEAN (&v) && ! g_value_get_boolean (&v)))
1405 0 : qof_instance_slot_delete (QOF_INSTANCE (account), "placeholder");
1406 :
1407 455 : g_value_unset (&v);
1408 455 : qof_instance_slot_delete_if_empty (QOF_INSTANCE (account), "hbci");
1409 455 : scrub_depth--;
1410 : }
1411 :
1412 : /* ================================================================ */
1413 :
1414 : void
1415 0 : xaccAccountScrubColorNotSet (QofBook *book)
1416 : {
1417 0 : GValue value_s = G_VALUE_INIT;
1418 : gboolean already_scrubbed;
1419 :
1420 : // get the run-once value
1421 0 : qof_instance_get_kvp (QOF_INSTANCE (book), &value_s, 1, "remove-color-not-set-slots");
1422 :
1423 0 : already_scrubbed = (G_VALUE_HOLDS_STRING (&value_s) &&
1424 0 : !g_strcmp0 (g_value_get_string (&value_s), "true"));
1425 0 : g_value_unset (&value_s);
1426 :
1427 0 : if (already_scrubbed)
1428 0 : return;
1429 : else
1430 : {
1431 0 : GValue value_b = G_VALUE_INIT;
1432 0 : Account *root = gnc_book_get_root_account (book);
1433 0 : GList *accts = gnc_account_get_descendants_sorted (root);
1434 : GList *ptr;
1435 :
1436 0 : for (ptr = accts; ptr; ptr = g_list_next (ptr))
1437 : {
1438 0 : auto acct = GNC_ACCOUNT(ptr->data);
1439 0 : auto color = xaccAccountGetColor (acct);
1440 :
1441 0 : if (g_strcmp0 (color, "Not Set") == 0)
1442 0 : xaccAccountSetColor (acct, "");
1443 : }
1444 0 : g_list_free (accts);
1445 :
1446 0 : g_value_init (&value_b, G_TYPE_BOOLEAN);
1447 0 : g_value_set_boolean (&value_b, TRUE);
1448 :
1449 : // set the run-once value
1450 0 : qof_instance_set_kvp (QOF_INSTANCE (book), &value_b, 1, "remove-color-not-set-slots");
1451 0 : g_value_unset (&value_b);
1452 : }
1453 : }
1454 :
1455 : /* ================================================================ */
1456 :
1457 : static Account*
1458 83 : construct_account (Account *root, gnc_commodity *currency, const char *accname,
1459 : GNCAccountType acctype, gboolean placeholder)
1460 : {
1461 83 : gnc_commodity* root_currency = find_root_currency ();
1462 83 : Account *acc = xaccMallocAccount(gnc_account_get_book (root));
1463 83 : xaccAccountBeginEdit (acc);
1464 83 : if (accname && *accname)
1465 83 : xaccAccountSetName (acc, accname);
1466 83 : if (currency || root_currency)
1467 83 : xaccAccountSetCommodity (acc, currency ? currency : root_currency);
1468 83 : xaccAccountSetType (acc, acctype);
1469 83 : xaccAccountSetPlaceholder (acc, placeholder);
1470 :
1471 : /* Hang the account off the root. */
1472 83 : gnc_account_append_child (root, acc);
1473 83 : xaccAccountCommitEdit (acc);
1474 83 : return acc;
1475 : }
1476 :
1477 : static Account*
1478 0 : find_root_currency_account_in_list (GList *acc_list)
1479 : {
1480 0 : gnc_commodity* root_currency = find_root_currency();
1481 0 : for (GList *node = acc_list; node; node = g_list_next (node))
1482 : {
1483 0 : Account *acc = GNC_ACCOUNT (node->data);
1484 0 : gnc_commodity *acc_commodity = nullptr;
1485 0 : if (G_UNLIKELY (!acc)) continue;
1486 0 : acc_commodity = xaccAccountGetCommodity(acc);
1487 0 : if (gnc_commodity_equiv (acc_commodity, root_currency))
1488 0 : return acc;
1489 : }
1490 :
1491 0 : return nullptr;
1492 : }
1493 :
1494 : static Account*
1495 0 : find_account_matching_name_in_list (GList *acc_list, const char* accname)
1496 : {
1497 0 : for (GList* node = acc_list; node; node = g_list_next(node))
1498 : {
1499 0 : Account *acc = GNC_ACCOUNT (node->data);
1500 0 : if (G_UNLIKELY (!acc)) continue;
1501 0 : if (g_strcmp0 (accname, xaccAccountGetName (acc)) == 0)
1502 0 : return acc;
1503 : }
1504 0 : return nullptr;
1505 : }
1506 :
1507 : Account *
1508 107 : xaccScrubUtilityGetOrMakeAccount (Account *root, gnc_commodity * currency,
1509 : const char *accname, GNCAccountType acctype,
1510 : gboolean placeholder, gboolean checkname)
1511 : {
1512 : GList* acc_list;
1513 107 : Account *acc = nullptr;
1514 :
1515 107 : g_return_val_if_fail (root, nullptr);
1516 :
1517 : acc_list =
1518 107 : gnc_account_lookup_by_type_and_commodity (root,
1519 : checkname ? accname : nullptr,
1520 : acctype, currency);
1521 :
1522 107 : if (!acc_list)
1523 83 : return construct_account (root, currency, accname,
1524 83 : acctype, placeholder);
1525 :
1526 24 : if (g_list_next(acc_list))
1527 : {
1528 0 : if (!currency)
1529 0 : acc = find_root_currency_account_in_list (acc_list);
1530 :
1531 0 : if (!acc)
1532 0 : acc = find_account_matching_name_in_list (acc_list, accname);
1533 : }
1534 :
1535 24 : if (!acc)
1536 24 : acc = GNC_ACCOUNT (acc_list->data);
1537 :
1538 24 : g_list_free (acc_list);
1539 24 : return acc;
1540 : }
1541 :
1542 : void
1543 791 : xaccTransScrubPostedDate (Transaction *trans)
1544 : {
1545 791 : time64 orig = xaccTransGetDate(trans);
1546 791 : if(orig == INT64_MAX)
1547 : {
1548 0 : GDate date = xaccTransGetDatePostedGDate(trans);
1549 0 : time64 time = gdate_to_time64(date);
1550 0 : if(time != INT64_MAX)
1551 : {
1552 : // xaccTransSetDatePostedSecs handles committing the change.
1553 0 : xaccTransSetDatePostedSecs(trans, time);
1554 : }
1555 : }
1556 791 : }
1557 :
1558 : /* ==================== END OF FILE ==================== */
|