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