Branch data 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)); }, false); };
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 : 1624 : TransScrubOrphansFast (Transaction *trans, Account *root)
112 : : {
113 : 1624 : g_return_if_fail (trans && trans->common_currency && root);
114 : :
115 : 4904 : for (GList *node = trans->splits; node; node = node->next)
116 : : {
117 : 3296 : Split *split = GNC_SPLIT(node->data);
118 : 3296 : if (abort_now) break;
119 : :
120 : 3296 : if (split->acc) continue;
121 : :
122 : 57 : DEBUG ("Found an orphan\n");
123 : :
124 : : gchar *accname = g_strconcat
125 : 57 : (_("Orphan"), "-", gnc_commodity_get_mnemonic (trans->common_currency),
126 : : nullptr);
127 : :
128 : : Account *orph = xaccScrubUtilityGetOrMakeAccount
129 : 57 : (root, trans->common_currency, accname, ACCT_TYPE_BANK, false, true);
130 : :
131 : 57 : g_free (accname);
132 : 57 : if (!orph) continue;
133 : :
134 : 57 : 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 : 1624 : xaccTransScrubOrphans (Transaction *trans)
180 : : {
181 : : SplitList *node;
182 : 1624 : QofBook *book = nullptr;
183 : 1624 : Account *root = nullptr;
184 : :
185 : 1624 : if (!trans) return;
186 : :
187 : 1692 : for (node = trans->splits; node; node = node->next)
188 : : {
189 : 1628 : Split *split = GNC_SPLIT(node->data);
190 : 1628 : if (abort_now) break;
191 : :
192 : 1628 : 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 : 64 : PINFO ("Free Floating Transaction!");
205 : 64 : book = xaccTransGetBook (trans);
206 : 64 : root = gnc_book_get_root_account (book);
207 : 64 : 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 : 2066 : 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 : 8359 : 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 : 8359 : if (!split) return false;
246 : 8359 : ENTER ("(split=%p)", split);
247 : :
248 : 8359 : trans = xaccSplitGetParent (split);
249 : 8359 : if (!trans)
250 : : {
251 : 8 : LEAVE("no trans");
252 : 8 : return false;
253 : : }
254 : :
255 : 8351 : 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 : 8351 : if (!account)
261 : : {
262 : 142 : if (dry_run)
263 : 71 : return true;
264 : : else
265 : 71 : xaccTransScrubOrphans (trans);
266 : 71 : 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 : 8280 : 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 : 8264 : value = xaccSplitGetValue (split);
282 : 8264 : 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 : 8264 : amount = xaccSplitGetAmount (split);
292 : 8264 : 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 : 8264 : currency = xaccTransGetCurrency (trans);
302 : :
303 : : /* If the account doesn't have a commodity,
304 : : * we should attempt to fix that first.
305 : : */
306 : 8264 : acc_commodity = xaccAccountGetCommodity(account);
307 : 8264 : if (!acc_commodity)
308 : : {
309 : 1362 : if (dry_run)
310 : 454 : return true;
311 : : else
312 : 908 : xaccAccountScrubCommodity (account);
313 : : }
314 : 7810 : if (!acc_commodity || !gnc_commodity_equiv(acc_commodity, currency))
315 : : {
316 : 2748 : LEAVE ("(split=%p) inequiv currency", split);
317 : 2748 : return false;
318 : : }
319 : :
320 : 5062 : scu = MIN (xaccAccountGetCommoditySCU (account),
321 : : gnc_commodity_get_fraction (currency));
322 : :
323 : 5062 : if (gnc_numeric_same (amount, value, scu, GNC_HOW_RND_ROUND_HALF_UP))
324 : : {
325 : 4995 : LEAVE("(split=%p) different values", split);
326 : 4995 : 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 : 3008 : xaccTransScrubSplits (Transaction *trans)
396 : : {
397 : 3008 : if (!trans) return;
398 : :
399 : 3008 : gnc_commodity *currency = xaccTransGetCurrency (trans);
400 : 3008 : if (!currency)
401 : 166 : PERR ("Transaction doesn't have a currency!");
402 : :
403 : 3008 : bool must_scrub = false;
404 : :
405 : 8625 : for (GList *n = xaccTransGetSplitList (trans); !must_scrub && n; n = g_list_next (n))
406 : 5617 : if (split_scrub_or_dry_run (GNC_SPLIT(n->data), true))
407 : 555 : must_scrub = true;
408 : :
409 : 3008 : if (!must_scrub)
410 : 2453 : return;
411 : :
412 : 555 : xaccTransBeginEdit(trans);
413 : : /* The split scrub expects the transaction to have a currency! */
414 : :
415 : 1641 : for (GList *n = xaccTransGetSplitList (trans); n; n = g_list_next (n))
416 : 1086 : xaccSplitScrub (GNC_SPLIT(n->data));
417 : :
418 : 555 : xaccTransCommitEdit(trans);
419 : : }
420 : :
421 : : /* ================================================================ */
422 : :
423 : : void
424 : 2742 : xaccSplitScrub (Split *split)
425 : : {
426 : 2742 : split_scrub_or_dry_run (split, false);
427 : 2742 : }
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 : 38 : get_balance_split (Transaction *trans, Account *root, Account *account,
446 : : gnc_commodity *commodity)
447 : : {
448 : : Split *balance_split;
449 : : gchar *accname;
450 : :
451 : 38 : if (!account ||
452 : 0 : !gnc_commodity_equiv (commodity, xaccAccountGetCommodity(account)))
453 : : {
454 : 38 : if (!root)
455 : : {
456 : 38 : root = gnc_book_get_root_account (xaccTransGetBook (trans));
457 : 38 : 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 : 38 : accname = g_strconcat (_("Imbalance"), "-",
465 : : gnc_commodity_get_mnemonic (commodity), nullptr);
466 : 38 : account = xaccScrubUtilityGetOrMakeAccount (root, commodity,
467 : : accname, ACCT_TYPE_BANK,
468 : : FALSE, TRUE);
469 : 38 : g_free (accname);
470 : 38 : if (!account)
471 : : {
472 : 0 : PERR ("Can't get balancing account");
473 : 0 : return nullptr;
474 : : }
475 : : }
476 : :
477 : 38 : balance_split = xaccTransFindSplitByAccount(trans, account);
478 : :
479 : : /* Put split into account before setting split value */
480 : 38 : if (!balance_split)
481 : : {
482 : 27 : balance_split = xaccMallocSplit (qof_instance_get_book(trans));
483 : :
484 : 27 : xaccTransBeginEdit (trans);
485 : 27 : xaccSplitSetParent(balance_split, trans);
486 : 27 : xaccSplitSetAccount(balance_split, account);
487 : 27 : xaccTransCommitEdit (trans);
488 : : }
489 : :
490 : 38 : return balance_split;
491 : : }
492 : :
493 : : static gnc_commodity*
494 : 79 : find_root_currency(void)
495 : : {
496 : 79 : QofSession *sess = gnc_get_current_session ();
497 : 79 : Account *root = gnc_book_get_root_account (qof_session_get_book (sess));
498 : 79 : 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 : 79 : if (!root_currency)
504 : : {
505 : 79 : GList *children = gnc_account_get_children (root);
506 : 79 : for (GList *node = children; node && !root_currency;
507 : 0 : node = g_list_next (node))
508 : : {
509 : 0 : Account *child = GNC_ACCOUNT (node->data);
510 : 0 : if (xaccAccountGetType (child) == ACCT_TYPE_INCOME)
511 : 0 : root_currency = xaccAccountGetCommodity (child);
512 : : }
513 : 79 : g_list_free (children);
514 : : }
515 : 79 : 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 : 38 : 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 : 38 : gnc_commodity *currency = xaccTransGetCurrency (trans);
589 : :
590 : 38 : balance_split = get_balance_split(trans, root, account, currency);
591 : 38 : if (!balance_split)
592 : : {
593 : : /* Error already logged */
594 : 0 : LEAVE("");
595 : 0 : return;
596 : : }
597 : 38 : account = xaccSplitGetAccount(balance_split);
598 : :
599 : 38 : xaccTransBeginEdit (trans);
600 : :
601 : 38 : old_value = xaccSplitGetValue (balance_split);
602 : :
603 : : /* Note: We have to round for the commodity's fraction, NOT any
604 : : * already existing denominator (bug #104343), because either one
605 : : * of the denominators might already be reduced. */
606 : 38 : new_value = gnc_numeric_sub (old_value, imbalance,
607 : 38 : gnc_commodity_get_fraction(currency),
608 : : GNC_HOW_RND_ROUND_HALF_UP);
609 : :
610 : 38 : xaccSplitSetValue (balance_split, new_value);
611 : :
612 : 38 : commodity = xaccAccountGetCommodity (account);
613 : 38 : if (gnc_commodity_equiv (currency, commodity))
614 : : {
615 : 38 : xaccSplitSetAmount (balance_split, new_value);
616 : : }
617 : :
618 : 38 : xaccSplitScrub (balance_split);
619 : 38 : xaccTransCommitEdit (trans);
620 : : }
621 : :
622 : : /* Balance a transaction without trading accounts. */
623 : : static void
624 : 38 : gnc_transaction_balance_no_trading (Transaction *trans, Account *root,
625 : : Account *account)
626 : : {
627 : 38 : gnc_numeric imbalance = xaccTransGetImbalanceValue (trans);
628 : :
629 : : /* Make the value sum to zero */
630 : 38 : if (! gnc_numeric_zero_p (imbalance))
631 : : {
632 : 38 : PINFO ("Value unbalanced transaction");
633 : :
634 : 38 : add_balance_split (trans, imbalance, root, account);
635 : : }
636 : :
637 : 38 : }
638 : :
639 : : static gnc_numeric
640 : 0 : gnc_transaction_get_commodity_imbalance (Transaction *trans,
641 : : gnc_commodity *commodity)
642 : : {
643 : : /* Find the value imbalance in this commodity */
644 : 0 : gnc_numeric val_imbalance = gnc_numeric_zero();
645 : 0 : GList *splits = nullptr;
646 : 0 : for (splits = trans->splits; splits; splits = splits->next)
647 : : {
648 : 0 : Split *split = GNC_SPLIT(splits->data);
649 : : gnc_commodity *split_commodity =
650 : 0 : xaccAccountGetCommodity(xaccSplitGetAccount(split));
651 : 0 : if (xaccTransStillHasSplit (trans, split) &&
652 : 0 : gnc_commodity_equal (commodity, split_commodity))
653 : 0 : val_imbalance = gnc_numeric_add (val_imbalance,
654 : : xaccSplitGetValue (split),
655 : : GNC_DENOM_AUTO,
656 : : GNC_HOW_DENOM_EXACT);
657 : : }
658 : 0 : return val_imbalance;
659 : : }
660 : :
661 : : /* GFunc wrapper for xaccSplitDestroy */
662 : : static void
663 : 0 : destroy_split (void* ptr)
664 : : {
665 : 0 : Split *split = GNC_SPLIT (ptr);
666 : 0 : if (split)
667 : 0 : xaccSplitDestroy (split);
668 : 0 : }
669 : :
670 : : /* Balancing transactions with trading accounts works best when
671 : : * starting with no trading splits.
672 : : */
673 : : static void
674 : 0 : xaccTransClearTradingSplits (Transaction *trans)
675 : : {
676 : 0 : GList *trading_splits = nullptr;
677 : :
678 : 0 : for (GList* node = trans->splits; node; node = node->next)
679 : : {
680 : 0 : Split* split = GNC_SPLIT(node->data);
681 : 0 : Account* acc = nullptr;
682 : 0 : if (!split)
683 : 0 : continue;
684 : 0 : acc = xaccSplitGetAccount(split);
685 : 0 : if (acc && xaccAccountGetType(acc) == ACCT_TYPE_TRADING)
686 : 0 : trading_splits = g_list_prepend (trading_splits, node->data);
687 : : }
688 : :
689 : 0 : if (!trading_splits)
690 : 0 : return;
691 : :
692 : 0 : xaccTransBeginEdit (trans);
693 : : /* destroy_splits doesn't actually free the splits but this gets
694 : : * the list itself freed.
695 : : */
696 : 0 : g_list_free_full (trading_splits, destroy_split);
697 : 0 : xaccTransCommitEdit (trans);
698 : : }
699 : :
700 : : static void
701 : 0 : gnc_transaction_balance_trading (Transaction *trans, Account *root)
702 : : {
703 : : MonetaryList *imbal_list;
704 : : MonetaryList *imbalance_commod;
705 : 0 : Split *balance_split = nullptr;
706 : :
707 : : /* If the transaction is balanced, nothing more to do */
708 : 0 : imbal_list = xaccTransGetImbalance (trans);
709 : 0 : if (!imbal_list)
710 : : {
711 : 0 : LEAVE("transaction is balanced");
712 : 0 : return;
713 : : }
714 : :
715 : 0 : PINFO ("Currency unbalanced transaction");
716 : :
717 : 0 : for (imbalance_commod = imbal_list; imbalance_commod;
718 : 0 : imbalance_commod = imbalance_commod->next)
719 : : {
720 : 0 : auto imbal_mon = static_cast<gnc_monetary*>(imbalance_commod->data);
721 : : gnc_commodity *commodity;
722 : : gnc_numeric old_amount, new_amount;
723 : 0 : const gnc_commodity *txn_curr = xaccTransGetCurrency (trans);
724 : :
725 : 0 : commodity = gnc_monetary_commodity (*imbal_mon);
726 : :
727 : 0 : balance_split = get_trading_split(trans, root, commodity);
728 : 0 : if (!balance_split)
729 : : {
730 : : /* Error already logged */
731 : 0 : gnc_monetary_list_free(imbal_list);
732 : 0 : LEAVE("");
733 : 0 : return;
734 : : }
735 : :
736 : 0 : xaccTransBeginEdit (trans);
737 : :
738 : 0 : old_amount = xaccSplitGetAmount (balance_split);
739 : 0 : new_amount = gnc_numeric_sub (old_amount, gnc_monetary_value(*imbal_mon),
740 : 0 : gnc_commodity_get_fraction(commodity),
741 : : GNC_HOW_RND_ROUND_HALF_UP);
742 : :
743 : 0 : xaccSplitSetAmount (balance_split, new_amount);
744 : :
745 : 0 : if (gnc_commodity_equal (txn_curr, commodity))
746 : : {
747 : : /* Imbalance commodity is the transaction currency, value in the
748 : : split must be the same as the amount */
749 : 0 : xaccSplitSetValue (balance_split, new_amount);
750 : : }
751 : : else
752 : : {
753 : 0 : gnc_numeric val_imbalance = gnc_transaction_get_commodity_imbalance (trans, commodity);
754 : :
755 : 0 : gnc_numeric old_value = xaccSplitGetValue (balance_split);
756 : 0 : gnc_numeric new_value = gnc_numeric_sub (old_value, val_imbalance,
757 : 0 : gnc_commodity_get_fraction(txn_curr),
758 : : GNC_HOW_RND_ROUND_HALF_UP);
759 : :
760 : 0 : xaccSplitSetValue (balance_split, new_value);
761 : : }
762 : :
763 : 0 : xaccSplitScrub (balance_split);
764 : 0 : xaccTransCommitEdit (trans);
765 : : }
766 : :
767 : 0 : gnc_monetary_list_free(imbal_list);
768 : : }
769 : :
770 : : /** Balance the transaction by adding more trading splits. This shouldn't
771 : : * ordinarily be necessary.
772 : : * @param trans the transaction to balance
773 : : * @param root the root account
774 : : */
775 : : static void
776 : 0 : gnc_transaction_balance_trading_more_splits (Transaction *trans, Account *root)
777 : : {
778 : : /* Copy the split list so we don't see the splits we're adding */
779 : 0 : GList *splits_dup = g_list_copy(trans->splits), *splits = nullptr;
780 : 0 : const gnc_commodity *txn_curr = xaccTransGetCurrency (trans);
781 : 0 : for (splits = splits_dup; splits; splits = splits->next)
782 : : {
783 : 0 : Split *split = GNC_SPLIT(splits->data);
784 : 0 : if (! xaccTransStillHasSplit(trans, split)) continue;
785 : 0 : if (!gnc_numeric_zero_p(xaccSplitGetValue(split)) &&
786 : 0 : gnc_numeric_zero_p(xaccSplitGetAmount(split)))
787 : : {
788 : : gnc_commodity *commodity;
789 : : gnc_numeric old_value, new_value;
790 : : Split *balance_split;
791 : :
792 : 0 : commodity = xaccAccountGetCommodity(xaccSplitGetAccount(split));
793 : 0 : if (!commodity)
794 : : {
795 : 0 : PERR("Split has no commodity");
796 : 0 : continue;
797 : : }
798 : 0 : balance_split = get_trading_split(trans, root, commodity);
799 : 0 : if (!balance_split)
800 : : {
801 : : /* Error already logged */
802 : 0 : LEAVE("");
803 : 0 : return;
804 : : }
805 : 0 : xaccTransBeginEdit (trans);
806 : :
807 : 0 : old_value = xaccSplitGetValue (balance_split);
808 : 0 : new_value = gnc_numeric_sub (old_value, xaccSplitGetValue(split),
809 : 0 : gnc_commodity_get_fraction(txn_curr),
810 : : GNC_HOW_RND_ROUND_HALF_UP);
811 : 0 : xaccSplitSetValue (balance_split, new_value);
812 : :
813 : : /* Don't change the balance split's amount since the amount
814 : : is zero in the split we're working on */
815 : :
816 : 0 : xaccSplitScrub (balance_split);
817 : 0 : xaccTransCommitEdit (trans);
818 : : }
819 : : }
820 : :
821 : 0 : g_list_free(splits_dup);
822 : : }
823 : :
824 : : /** Correct transaction imbalances.
825 : : * @param trans The Transaction
826 : : * @param root The (hidden) root account, for the book default currency.
827 : : * @param account The account whose currency in which to balance.
828 : : */
829 : :
830 : : void
831 : 3008 : xaccTransScrubImbalance (Transaction *trans, Account *root,
832 : : Account *account)
833 : : {
834 : : gnc_numeric imbalance;
835 : :
836 : 6016 : if (!trans) return;
837 : :
838 : 3008 : ENTER ("()");
839 : :
840 : : /* Must look for orphan splits even if there is no imbalance. */
841 : 3008 : xaccTransScrubSplits (trans);
842 : :
843 : : /* Return immediately if things are balanced. */
844 : 3008 : if (xaccTransIsBalanced (trans))
845 : : {
846 : 2970 : LEAVE ("transaction is balanced");
847 : 2970 : return;
848 : : }
849 : :
850 : 38 : if (! xaccTransUseTradingAccounts (trans))
851 : : {
852 : 38 : gnc_transaction_balance_no_trading (trans, root, account);
853 : 38 : LEAVE ("transaction balanced, no managed trading accounts");
854 : 38 : return;
855 : : }
856 : :
857 : 0 : xaccTransClearTradingSplits (trans);
858 : 0 : imbalance = xaccTransGetImbalanceValue (trans);
859 : 0 : if (! gnc_numeric_zero_p (imbalance))
860 : : {
861 : 0 : PINFO ("Value unbalanced transaction");
862 : :
863 : 0 : add_balance_split (trans, imbalance, root, account);
864 : : }
865 : :
866 : 0 : gnc_transaction_balance_trading (trans, root);
867 : 0 : if (gnc_numeric_zero_p(xaccTransGetImbalanceValue(trans)))
868 : : {
869 : 0 : LEAVE ("()");
870 : 0 : return;
871 : : }
872 : : /* If the transaction is still not balanced, it's probably because there
873 : : are splits with zero amount and non-zero value. These are usually
874 : : realized gain/loss splits. Add a reversing split for each of them to
875 : : balance the value. */
876 : :
877 : 0 : gnc_transaction_balance_trading_more_splits (trans, root);
878 : 0 : if (!gnc_numeric_zero_p(xaccTransGetImbalanceValue(trans)))
879 : 0 : PERR("Balancing currencies unbalanced value");
880 : :
881 : : }
882 : :
883 : : /* ================================================================ */
884 : : /* The xaccTransFindCommonCurrency () method returns
885 : : * a gnc_commodity indicating a currency denomination that all
886 : : * of the splits in this transaction have in common, using the
887 : : * old/obsolete currency/security fields of the split accounts.
888 : : */
889 : :
890 : : static gnc_commodity *
891 : 0 : FindCommonExclSCurrency (SplitList *splits,
892 : : gnc_commodity * ra, gnc_commodity * rb,
893 : : Split *excl_split)
894 : : {
895 : : GList *node;
896 : :
897 : 0 : if (!splits) return nullptr;
898 : :
899 : 0 : for (node = splits; node; node = node->next)
900 : : {
901 : 0 : Split *s = GNC_SPLIT(node->data);
902 : : gnc_commodity * sa, * sb;
903 : :
904 : 0 : if (s == excl_split) continue;
905 : :
906 : 0 : g_return_val_if_fail (s->acc, nullptr);
907 : :
908 : 0 : sa = DxaccAccountGetCurrency (s->acc);
909 : 0 : sb = xaccAccountGetCommodity (s->acc);
910 : :
911 : 0 : if (ra && rb)
912 : : {
913 : 0 : int aa = !gnc_commodity_equiv(ra, sa);
914 : 0 : int ab = !gnc_commodity_equiv(ra, sb);
915 : 0 : int ba = !gnc_commodity_equiv(rb, sa);
916 : 0 : int bb = !gnc_commodity_equiv(rb, sb);
917 : :
918 : 0 : if ( (!aa) && bb) rb = nullptr;
919 : 0 : else if ( (!ab) && ba) rb = nullptr;
920 : 0 : else if ( (!ba) && ab) ra = nullptr;
921 : 0 : else if ( (!bb) && aa) ra = nullptr;
922 : 0 : else if ( aa && bb && ab && ba )
923 : : {
924 : 0 : ra = nullptr;
925 : 0 : rb = nullptr;
926 : : }
927 : :
928 : 0 : if (!ra)
929 : : {
930 : 0 : ra = rb;
931 : 0 : rb = nullptr;
932 : : }
933 : 0 : }
934 : 0 : else if (ra && !rb)
935 : : {
936 : 0 : int aa = !gnc_commodity_equiv(ra, sa);
937 : 0 : int ab = !gnc_commodity_equiv(ra, sb);
938 : 0 : if ( aa && ab ) ra = nullptr;
939 : 0 : }
940 : 0 : else if (!ra && rb)
941 : : {
942 : 0 : int aa = !gnc_commodity_equiv(rb, sa);
943 : 0 : int ab = !gnc_commodity_equiv(rb, sb);
944 : 0 : ra = ( aa && ab ) ? nullptr : rb;
945 : : }
946 : :
947 : 0 : if ((!ra) && (!rb)) return nullptr;
948 : : }
949 : :
950 : 0 : return (ra);
951 : : }
952 : :
953 : : /* This is the wrapper for those calls (i.e. the older ones) which
954 : : * don't exclude one split from the splitlist when looking for a
955 : : * common currency.
956 : : */
957 : : static gnc_commodity *
958 : 0 : FindCommonCurrency (GList *splits, gnc_commodity * ra, gnc_commodity * rb)
959 : : {
960 : 0 : return FindCommonExclSCurrency(splits, ra, rb, nullptr);
961 : : }
962 : :
963 : : static gnc_commodity *
964 : 0 : xaccTransFindOldCommonCurrency (Transaction *trans, QofBook *book)
965 : : {
966 : : gnc_commodity *ra, *rb, *retval;
967 : : Split *split;
968 : :
969 : 0 : if (!trans) return nullptr;
970 : :
971 : 0 : if (trans->splits == nullptr) return nullptr;
972 : :
973 : 0 : g_return_val_if_fail (book, nullptr);
974 : :
975 : 0 : split = GNC_SPLIT(trans->splits->data);
976 : :
977 : 0 : if (!split || nullptr == split->acc) return nullptr;
978 : :
979 : 0 : ra = DxaccAccountGetCurrency (split->acc);
980 : 0 : rb = xaccAccountGetCommodity (split->acc);
981 : :
982 : 0 : retval = FindCommonCurrency (trans->splits, ra, rb);
983 : :
984 : 0 : if (retval && !gnc_commodity_is_currency(retval))
985 : 0 : retval = nullptr;
986 : :
987 : 0 : return retval;
988 : : }
989 : :
990 : : /* Test the currency of the splits and find the most common and return
991 : : * it, or nullptr if there is no currency more common than the
992 : : * others -- or none at all.
993 : : */
994 : : typedef struct
995 : : {
996 : : gnc_commodity *commodity;
997 : : unsigned int count;
998 : : } CommodityCount;
999 : :
1000 : : static gint
1001 : 202 : commodity_equal (gconstpointer a, gconstpointer b)
1002 : : {
1003 : 202 : CommodityCount *cc = (CommodityCount*)a;
1004 : 202 : gnc_commodity *com = (gnc_commodity*)b;
1005 : 404 : if ( cc == nullptr || cc->commodity == nullptr ||
1006 : 202 : !GNC_IS_COMMODITY( cc->commodity ) ) return -1;
1007 : 202 : if ( com == nullptr || !GNC_IS_COMMODITY( com ) ) return 1;
1008 : 202 : if ( gnc_commodity_equal(cc->commodity, com) )
1009 : 202 : return 0;
1010 : 0 : return 1;
1011 : : }
1012 : :
1013 : : static gint
1014 : 0 : commodity_compare( gconstpointer a, gconstpointer b)
1015 : : {
1016 : 0 : CommodityCount *ca = (CommodityCount*)a, *cb = (CommodityCount*)b;
1017 : 0 : if (ca == nullptr || ca->commodity == nullptr ||
1018 : 0 : !GNC_IS_COMMODITY( ca->commodity ) )
1019 : : {
1020 : 0 : if (cb == nullptr || cb->commodity == nullptr ||
1021 : 0 : !GNC_IS_COMMODITY( cb->commodity ) )
1022 : 0 : return 0;
1023 : 0 : return -1;
1024 : : }
1025 : 0 : if (cb == nullptr || cb->commodity == nullptr ||
1026 : 0 : !GNC_IS_COMMODITY( cb->commodity ) )
1027 : 0 : return 1;
1028 : 0 : if (ca->count == cb->count)
1029 : 0 : return 0;
1030 : 0 : return ca->count > cb->count ? 1 : -1;
1031 : : }
1032 : :
1033 : : /* Find the commodities in the account of each of the splits of a
1034 : : * transaction, and rank them by how many splits in which they
1035 : : * occur. Commodities which are currencies count more than those which
1036 : : * aren't, because for simple buy and sell transactions it makes
1037 : : * slightly more sense for the transaction commodity to be the
1038 : : * currency -- to the extent that it makes sense for a transaction to
1039 : : * have a currency at all. jralls, 2010-11-02 */
1040 : :
1041 : : static gnc_commodity *
1042 : 198 : xaccTransFindCommonCurrency (Transaction *trans, QofBook *book)
1043 : : {
1044 : : gnc_commodity *com_scratch;
1045 : 198 : GList *node = nullptr;
1046 : 198 : GSList *comlist = nullptr, *found = nullptr;
1047 : :
1048 : 198 : if (!trans) return nullptr;
1049 : :
1050 : 198 : if (trans->splits == nullptr) return nullptr;
1051 : :
1052 : 198 : g_return_val_if_fail (book, nullptr);
1053 : :
1054 : : /* Find the most commonly used currency among the splits. If a given split
1055 : : is in a non-currency commodity, then look for an ancestor account in a
1056 : : currency, but prefer currencies used directly in splits. Ignore trading
1057 : : account splits in this whole process, they don't add any value to this algorithm. */
1058 : 601 : for (node = trans->splits; node; node = node->next)
1059 : : {
1060 : 403 : Split *s = GNC_SPLIT(node->data);
1061 : : unsigned int curr_weight;
1062 : :
1063 : 403 : if (s == nullptr || s->acc == nullptr) continue;
1064 : 403 : if (xaccAccountGetType(s->acc) == ACCT_TYPE_TRADING) continue;
1065 : 403 : com_scratch = xaccAccountGetCommodity(s->acc);
1066 : 403 : if (com_scratch && gnc_commodity_is_currency(com_scratch))
1067 : : {
1068 : 71 : curr_weight = 3;
1069 : : }
1070 : : else
1071 : : {
1072 : 332 : com_scratch = gnc_account_get_currency_or_parent(s->acc);
1073 : 332 : if (com_scratch == nullptr) continue;
1074 : 329 : curr_weight = 1;
1075 : : }
1076 : 400 : if ( comlist )
1077 : : {
1078 : 202 : found = g_slist_find_custom(comlist, com_scratch, commodity_equal);
1079 : : }
1080 : 400 : if (comlist == nullptr || found == nullptr)
1081 : : {
1082 : 198 : CommodityCount *count = g_slice_new0(CommodityCount);
1083 : 198 : count->commodity = com_scratch;
1084 : 198 : count->count = curr_weight;
1085 : 198 : comlist = g_slist_append(comlist, count);
1086 : 198 : }
1087 : : else
1088 : : {
1089 : 202 : CommodityCount *count = (CommodityCount*)(found->data);
1090 : 202 : count->count += curr_weight;
1091 : : }
1092 : : }
1093 : 198 : found = g_slist_sort( comlist, commodity_compare);
1094 : :
1095 : 198 : if ( found && found->data && (((CommodityCount*)(found->data))->commodity != nullptr))
1096 : : {
1097 : 198 : return ((CommodityCount*)(found->data))->commodity;
1098 : : }
1099 : : /* We didn't find a currency in the current account structure, so try
1100 : : * an old one. */
1101 : 0 : return xaccTransFindOldCommonCurrency( trans, book );
1102 : : }
1103 : :
1104 : : /* ================================================================ */
1105 : :
1106 : : void
1107 : 1553 : xaccTransScrubCurrency (Transaction *trans)
1108 : : {
1109 : : SplitList *node;
1110 : : gnc_commodity *currency;
1111 : :
1112 : 1553 : if (!trans) return;
1113 : :
1114 : : /* If there are any orphaned splits in a transaction, then the
1115 : : * this routine will fail. Therefore, we want to make sure that
1116 : : * there are no orphans (splits without parent account).
1117 : : */
1118 : 1553 : xaccTransScrubOrphans (trans);
1119 : :
1120 : 1553 : currency = xaccTransGetCurrency (trans);
1121 : 1553 : if (currency && gnc_commodity_is_currency(currency)) return;
1122 : :
1123 : 198 : currency = xaccTransFindCommonCurrency (trans, qof_instance_get_book(trans));
1124 : 198 : if (currency)
1125 : : {
1126 : 198 : xaccTransBeginEdit (trans);
1127 : 198 : xaccTransSetCurrency (trans, currency);
1128 : 198 : xaccTransCommitEdit (trans);
1129 : : }
1130 : : else
1131 : : {
1132 : 0 : if (nullptr == trans->splits)
1133 : : {
1134 : 0 : PWARN ("Transaction \"%s\" has no splits in it!", trans->description);
1135 : : }
1136 : : else
1137 : : {
1138 : : SplitList *node;
1139 : : char guid_str[GUID_ENCODING_LENGTH + 1];
1140 : 0 : guid_to_string_buff(xaccTransGetGUID(trans), guid_str);
1141 : 0 : PWARN ("no common transaction currency found for trans=\"%s\" (%s);",
1142 : : trans->description, guid_str);
1143 : :
1144 : 0 : for (node = trans->splits; node; node = node->next)
1145 : : {
1146 : 0 : Split *split = GNC_SPLIT(node->data);
1147 : 0 : if (nullptr == split->acc)
1148 : : {
1149 : 0 : PWARN (" split=\"%s\" is not in any account!", split->memo);
1150 : : }
1151 : : else
1152 : : {
1153 : 0 : gnc_commodity *currency = xaccAccountGetCommodity(split->acc);
1154 : 0 : PWARN ("setting to split=\"%s\" account=\"%s\" commodity=\"%s\"",
1155 : : split->memo, xaccAccountGetName(split->acc),
1156 : : gnc_commodity_get_mnemonic(currency));
1157 : :
1158 : 0 : xaccTransBeginEdit (trans);
1159 : 0 : xaccTransSetCurrency (trans, currency);
1160 : 0 : xaccTransCommitEdit (trans);
1161 : 0 : return;
1162 : : }
1163 : : }
1164 : : }
1165 : 0 : return;
1166 : : }
1167 : :
1168 : 601 : for (node = trans->splits; node; node = node->next)
1169 : : {
1170 : 403 : Split *sp = GNC_SPLIT(node->data);
1171 : :
1172 : 403 : if (!gnc_numeric_equal(xaccSplitGetAmount (sp),
1173 : : xaccSplitGetValue (sp)))
1174 : : {
1175 : : gnc_commodity *acc_currency;
1176 : :
1177 : 299 : acc_currency = sp->acc ? xaccAccountGetCommodity(sp->acc) : nullptr;
1178 : 299 : if (acc_currency == currency)
1179 : : {
1180 : : /* This Split needs fixing: The transaction-currency equals
1181 : : * the account-currency/commodity, but the amount/values are
1182 : : * inequal i.e. they still correspond to the security
1183 : : * (amount) and the currency (value). In the new model, the
1184 : : * value is the amount in the account-commodity -- so it
1185 : : * needs to be set to equal the amount (since the
1186 : : * account-currency doesn't exist anymore).
1187 : : *
1188 : : * Note: Nevertheless we lose some information here. Namely,
1189 : : * the information that the 'amount' in 'account-old-security'
1190 : : * was worth 'value' in 'account-old-currency'. Maybe it would
1191 : : * be better to store that information in the price database?
1192 : : * But then, for old currency transactions there is still the
1193 : : * 'other' transaction, which is going to keep that
1194 : : * information. So I don't bother with that here. -- cstim,
1195 : : * 2002/11/20. */
1196 : :
1197 : 0 : PWARN ("Adjusted split with mismatched values, desc=\"%s\" memo=\"%s\""
1198 : : " old amount %s %s, new amount %s",
1199 : : trans->description, sp->memo,
1200 : : gnc_num_dbg_to_string (xaccSplitGetAmount(sp)),
1201 : : gnc_commodity_get_mnemonic (currency),
1202 : : gnc_num_dbg_to_string (xaccSplitGetValue(sp)));
1203 : 0 : xaccTransBeginEdit (trans);
1204 : 0 : xaccSplitSetAmount (sp, xaccSplitGetValue(sp));
1205 : 0 : xaccTransCommitEdit (trans);
1206 : : }
1207 : : /*else
1208 : : {
1209 : : PINFO ("Ok: Split '%s' Amount %s %s, value %s %s",
1210 : : xaccSplitGetMemo (sp),
1211 : : gnc_num_dbg_to_string (amount),
1212 : : gnc_commodity_get_mnemonic (currency),
1213 : : gnc_num_dbg_to_string (value),
1214 : : gnc_commodity_get_mnemonic (acc_currency));
1215 : : }*/
1216 : : }
1217 : : }
1218 : :
1219 : : }
1220 : :
1221 : : /* ================================================================ */
1222 : :
1223 : : void
1224 : 2543 : xaccAccountScrubCommodity (Account *account)
1225 : : {
1226 : : gnc_commodity *commodity;
1227 : :
1228 : 2543 : if (!account) return;
1229 : 2543 : if (xaccAccountGetType(account) == ACCT_TYPE_ROOT) return;
1230 : :
1231 : 2466 : commodity = xaccAccountGetCommodity (account);
1232 : 2466 : if (commodity) return;
1233 : :
1234 : : /* Use the 'obsolete' routines to try to figure out what the
1235 : : * account commodity should have been. */
1236 : 908 : commodity = xaccAccountGetCommodity (account);
1237 : 908 : if (commodity)
1238 : : {
1239 : 0 : xaccAccountSetCommodity (account, commodity);
1240 : 0 : return;
1241 : : }
1242 : :
1243 : 908 : commodity = DxaccAccountGetCurrency (account);
1244 : 908 : if (commodity)
1245 : : {
1246 : 0 : xaccAccountSetCommodity (account, commodity);
1247 : 0 : return;
1248 : : }
1249 : :
1250 : 908 : PERR ("Account \"%s\" does not have a commodity!",
1251 : : xaccAccountGetName(account));
1252 : : }
1253 : :
1254 : : /* ================================================================ */
1255 : :
1256 : : /* EFFECTIVE FRIEND FUNCTION declared in qofinstance-p.h */
1257 : : extern void qof_instance_set_dirty (QofInstance*);
1258 : :
1259 : : static void
1260 : 456 : xaccAccountDeleteOldData (Account *account)
1261 : : {
1262 : 456 : if (!account) return;
1263 : 456 : xaccAccountBeginEdit (account);
1264 : 456 : qof_instance_set_kvp (QOF_INSTANCE (account), nullptr, 1, "old-currency");
1265 : 456 : qof_instance_set_kvp (QOF_INSTANCE (account), nullptr, 1, "old-security");
1266 : 456 : qof_instance_set_kvp (QOF_INSTANCE (account), nullptr, 1, "old-currency-scu");
1267 : 456 : qof_instance_set_kvp (QOF_INSTANCE (account), nullptr, 1, "old-security-scu");
1268 : 456 : qof_instance_set_dirty (QOF_INSTANCE (account));
1269 : 456 : xaccAccountCommitEdit (account);
1270 : : }
1271 : :
1272 : : static int
1273 : 774 : scrub_trans_currency_helper (Transaction *t, gpointer data)
1274 : : {
1275 : 774 : xaccTransScrubCurrency (t);
1276 : 774 : return 0;
1277 : : }
1278 : :
1279 : : static void
1280 : 456 : scrub_account_commodity_helper (Account *account, gpointer data)
1281 : : {
1282 : 456 : scrub_depth++;
1283 : 456 : xaccAccountScrubCommodity (account);
1284 : 456 : xaccAccountDeleteOldData (account);
1285 : 456 : scrub_depth--;
1286 : 456 : }
1287 : :
1288 : : void
1289 : 22 : xaccAccountTreeScrubCommodities (Account *acc)
1290 : : {
1291 : 22 : if (!acc) return;
1292 : 22 : scrub_depth++;
1293 : 22 : xaccAccountTreeForEachTransaction (acc, scrub_trans_currency_helper, nullptr);
1294 : :
1295 : 22 : scrub_account_commodity_helper (acc, nullptr);
1296 : 22 : gnc_account_foreach_descendant (acc, scrub_account_commodity_helper, nullptr);
1297 : 22 : scrub_depth--;
1298 : : }
1299 : :
1300 : : /* ================================================================ */
1301 : :
1302 : : static gboolean
1303 : 5080 : check_quote_source (gnc_commodity *com, gpointer data)
1304 : : {
1305 : 5080 : gboolean *commodity_has_quote_src = (gboolean *)data;
1306 : 5080 : if (com && !gnc_commodity_is_iso(com))
1307 : 86 : *commodity_has_quote_src |= gnc_commodity_get_quote_flag(com);
1308 : 5080 : return TRUE;
1309 : : }
1310 : :
1311 : : static void
1312 : 456 : move_quote_source (Account *account, gpointer data)
1313 : : {
1314 : : gnc_commodity *com;
1315 : : gnc_quote_source *quote_source;
1316 : 456 : gboolean new_style = GPOINTER_TO_INT(data);
1317 : : const char *source, *tz;
1318 : :
1319 : 456 : com = xaccAccountGetCommodity(account);
1320 : 456 : if (!com)
1321 : 19 : return;
1322 : :
1323 : 437 : if (!new_style)
1324 : : {
1325 : 424 : source = dxaccAccountGetPriceSrc(account);
1326 : 424 : if (!source || !*source)
1327 : 424 : return;
1328 : 0 : tz = dxaccAccountGetQuoteTZ(account);
1329 : :
1330 : 0 : PINFO("to %8s from %s", gnc_commodity_get_mnemonic(com),
1331 : : xaccAccountGetName(account));
1332 : 0 : gnc_commodity_set_quote_flag(com, TRUE);
1333 : 0 : quote_source = gnc_quote_source_lookup_by_internal(source);
1334 : 0 : if (!quote_source)
1335 : 0 : quote_source = gnc_quote_source_add_new(source, FALSE);
1336 : 0 : gnc_commodity_set_quote_source(com, quote_source);
1337 : 0 : gnc_commodity_set_quote_tz(com, tz);
1338 : : }
1339 : :
1340 : 13 : dxaccAccountSetPriceSrc(account, nullptr);
1341 : 13 : dxaccAccountSetQuoteTZ(account, nullptr);
1342 : 13 : return;
1343 : : }
1344 : :
1345 : :
1346 : : void
1347 : 22 : xaccAccountTreeScrubQuoteSources (Account *root, gnc_commodity_table *table)
1348 : : {
1349 : 22 : gboolean new_style = FALSE;
1350 : 22 : ENTER(" ");
1351 : :
1352 : 22 : if (!root || !table)
1353 : : {
1354 : 0 : LEAVE("Oops");
1355 : 0 : return;
1356 : : }
1357 : 22 : scrub_depth++;
1358 : 22 : gnc_commodity_table_foreach_commodity (table, check_quote_source, &new_style);
1359 : :
1360 : 22 : move_quote_source(root, GINT_TO_POINTER(new_style));
1361 : 22 : gnc_account_foreach_descendant (root, move_quote_source,
1362 : 22 : GINT_TO_POINTER(new_style));
1363 : 22 : LEAVE("Migration done");
1364 : 22 : scrub_depth--;
1365 : : }
1366 : :
1367 : : /* ================================================================ */
1368 : :
1369 : : void
1370 : 455 : xaccAccountScrubKvp (Account *account)
1371 : : {
1372 : 455 : GValue v = G_VALUE_INIT;
1373 : : gchar *str2;
1374 : :
1375 : 455 : if (!account) return;
1376 : 455 : scrub_depth++;
1377 : :
1378 : 455 : qof_instance_get_kvp (QOF_INSTANCE (account), &v, 1, "notes");
1379 : 455 : if (G_VALUE_HOLDS_STRING (&v))
1380 : : {
1381 : 1 : str2 = g_strstrip(g_value_dup_string(&v));
1382 : 1 : if (strlen(str2) == 0)
1383 : 0 : qof_instance_slot_delete (QOF_INSTANCE (account), "notes");
1384 : 1 : g_free(str2);
1385 : : }
1386 : :
1387 : 455 : qof_instance_get_kvp (QOF_INSTANCE (account), &v, 1, "placeholder");
1388 : 455 : if ((G_VALUE_HOLDS_STRING (&v) &&
1389 : 910 : strcmp(g_value_get_string (&v), "false") == 0) ||
1390 : 455 : (G_VALUE_HOLDS_BOOLEAN (&v) && ! g_value_get_boolean (&v)))
1391 : 0 : qof_instance_slot_delete (QOF_INSTANCE (account), "placeholder");
1392 : :
1393 : 455 : g_value_unset (&v);
1394 : 455 : qof_instance_slot_delete_if_empty (QOF_INSTANCE (account), "hbci");
1395 : 455 : scrub_depth--;
1396 : : }
1397 : :
1398 : : /* ================================================================ */
1399 : :
1400 : : void
1401 : 0 : xaccAccountScrubColorNotSet (QofBook *book)
1402 : : {
1403 : 0 : GValue value_s = G_VALUE_INIT;
1404 : : gboolean already_scrubbed;
1405 : :
1406 : : // get the run-once value
1407 : 0 : qof_instance_get_kvp (QOF_INSTANCE (book), &value_s, 1, "remove-color-not-set-slots");
1408 : :
1409 : 0 : already_scrubbed = (G_VALUE_HOLDS_STRING (&value_s) &&
1410 : 0 : !g_strcmp0 (g_value_get_string (&value_s), "true"));
1411 : 0 : g_value_unset (&value_s);
1412 : :
1413 : 0 : if (already_scrubbed)
1414 : 0 : return;
1415 : : else
1416 : : {
1417 : 0 : GValue value_b = G_VALUE_INIT;
1418 : 0 : Account *root = gnc_book_get_root_account (book);
1419 : 0 : GList *accts = gnc_account_get_descendants_sorted (root);
1420 : : GList *ptr;
1421 : :
1422 : 0 : for (ptr = accts; ptr; ptr = g_list_next (ptr))
1423 : : {
1424 : 0 : auto acct = GNC_ACCOUNT(ptr->data);
1425 : 0 : auto color = xaccAccountGetColor (acct);
1426 : :
1427 : 0 : if (g_strcmp0 (color, "Not Set") == 0)
1428 : 0 : xaccAccountSetColor (acct, "");
1429 : : }
1430 : 0 : g_list_free (accts);
1431 : :
1432 : 0 : g_value_init (&value_b, G_TYPE_BOOLEAN);
1433 : 0 : g_value_set_boolean (&value_b, TRUE);
1434 : :
1435 : : // set the run-once value
1436 : 0 : qof_instance_set_kvp (QOF_INSTANCE (book), &value_b, 1, "remove-color-not-set-slots");
1437 : 0 : g_value_unset (&value_b);
1438 : : }
1439 : : }
1440 : :
1441 : : /* ================================================================ */
1442 : :
1443 : : static Account*
1444 : 79 : construct_account (Account *root, gnc_commodity *currency, const char *accname,
1445 : : GNCAccountType acctype, gboolean placeholder)
1446 : : {
1447 : 79 : gnc_commodity* root_currency = find_root_currency ();
1448 : 79 : Account *acc = xaccMallocAccount(gnc_account_get_book (root));
1449 : 79 : xaccAccountBeginEdit (acc);
1450 : 79 : if (accname && *accname)
1451 : 79 : xaccAccountSetName (acc, accname);
1452 : 79 : if (currency || root_currency)
1453 : 79 : xaccAccountSetCommodity (acc, currency ? currency : root_currency);
1454 : 79 : xaccAccountSetType (acc, acctype);
1455 : 79 : xaccAccountSetPlaceholder (acc, placeholder);
1456 : :
1457 : : /* Hang the account off the root. */
1458 : 79 : gnc_account_append_child (root, acc);
1459 : 79 : xaccAccountCommitEdit (acc);
1460 : 79 : return acc;
1461 : : }
1462 : :
1463 : : static Account*
1464 : 0 : find_root_currency_account_in_list (GList *acc_list)
1465 : : {
1466 : 0 : gnc_commodity* root_currency = find_root_currency();
1467 : 0 : for (GList *node = acc_list; node; node = g_list_next (node))
1468 : : {
1469 : 0 : Account *acc = GNC_ACCOUNT (node->data);
1470 : 0 : gnc_commodity *acc_commodity = nullptr;
1471 : 0 : if (G_UNLIKELY (!acc)) continue;
1472 : 0 : acc_commodity = xaccAccountGetCommodity(acc);
1473 : 0 : if (gnc_commodity_equiv (acc_commodity, root_currency))
1474 : 0 : return acc;
1475 : : }
1476 : :
1477 : 0 : return nullptr;
1478 : : }
1479 : :
1480 : : static Account*
1481 : 0 : find_account_matching_name_in_list (GList *acc_list, const char* accname)
1482 : : {
1483 : 0 : for (GList* node = acc_list; node; node = g_list_next(node))
1484 : : {
1485 : 0 : Account *acc = GNC_ACCOUNT (node->data);
1486 : 0 : if (G_UNLIKELY (!acc)) continue;
1487 : 0 : if (g_strcmp0 (accname, xaccAccountGetName (acc)) == 0)
1488 : 0 : return acc;
1489 : : }
1490 : 0 : return nullptr;
1491 : : }
1492 : :
1493 : : Account *
1494 : 95 : xaccScrubUtilityGetOrMakeAccount (Account *root, gnc_commodity * currency,
1495 : : const char *accname, GNCAccountType acctype,
1496 : : gboolean placeholder, gboolean checkname)
1497 : : {
1498 : : GList* acc_list;
1499 : 95 : Account *acc = nullptr;
1500 : :
1501 : 95 : g_return_val_if_fail (root, nullptr);
1502 : :
1503 : : acc_list =
1504 : 95 : gnc_account_lookup_by_type_and_commodity (root,
1505 : : checkname ? accname : nullptr,
1506 : : acctype, currency);
1507 : :
1508 : 95 : if (!acc_list)
1509 : 79 : return construct_account (root, currency, accname,
1510 : 79 : acctype, placeholder);
1511 : :
1512 : 16 : if (g_list_next(acc_list))
1513 : : {
1514 : 0 : if (!currency)
1515 : 0 : acc = find_root_currency_account_in_list (acc_list);
1516 : :
1517 : 0 : if (!acc)
1518 : 0 : acc = find_account_matching_name_in_list (acc_list, accname);
1519 : : }
1520 : :
1521 : 16 : if (!acc)
1522 : 16 : acc = GNC_ACCOUNT (acc_list->data);
1523 : :
1524 : 16 : g_list_free (acc_list);
1525 : 16 : return acc;
1526 : : }
1527 : :
1528 : : void
1529 : 788 : xaccTransScrubPostedDate (Transaction *trans)
1530 : : {
1531 : 788 : time64 orig = xaccTransGetDate(trans);
1532 : 788 : if(orig == INT64_MAX)
1533 : : {
1534 : 0 : GDate date = xaccTransGetDatePostedGDate(trans);
1535 : 0 : time64 time = gdate_to_time64(date);
1536 : 0 : if(time != INT64_MAX)
1537 : : {
1538 : : // xaccTransSetDatePostedSecs handles committing the change.
1539 : 0 : xaccTransSetDatePostedSecs(trans, time);
1540 : : }
1541 : : }
1542 : 788 : }
1543 : :
1544 : : /* ==================== END OF FILE ==================== */
|