GnuCash fec5643+
Loading...
Searching...
No Matches
Account.cpp
1/********************************************************************\
2 * Account.c -- Account data structure implementation *
3 * Copyright (C) 1997 Robin D. Clark *
4 * Copyright (C) 1997-2003 Linas Vepstas <linas@linas.org> *
5 * Copyright (C) 2007 David Hampton <hampton@employees.org> *
6 * *
7 * This program is free software; you can redistribute it and/or *
8 * modify it under the terms of the GNU General Public License as *
9 * published by the Free Software Foundation; either version 2 of *
10 * the License, or (at your option) any later version. *
11 * *
12 * This program is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU General Public License*
18 * along with this program; if not, contact: *
19 * *
20 * Free Software Foundation Voice: +1-617-542-5942 *
21 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
22 * Boston, MA 02110-1301, USA gnu@gnu.org *
23 * *
24\********************************************************************/
25
26#include <config.h>
27
28#include "gnc-prefs.h"
29
30#include <glib.h>
31#include <glib/gi18n.h>
32#include <stdlib.h>
33#include <stdint.h>
34#include <string.h>
35
36#include "AccountP.hpp"
37#include "Account.hpp"
38#include "Split.h"
39#include "Transaction.h"
40#include "TransactionP.hpp"
41#include "gnc-event.h"
42#include "gnc-glib-utils.h"
43#include "gnc-lot.h"
44#include "gnc-pricedb.h"
45#include "qofevent.h"
46#include "qofinstance-p.h"
47#include "gnc-features.h"
48#include "guid.hpp"
49
50#include <numeric>
51#include <map>
52#include <unordered_set>
53#include <algorithm>
54
55static QofLogModule log_module = GNC_MOD_ACCOUNT;
56
57/* The Canonical Account Separator. Pre-Initialized. */
58static gchar account_separator[8] = ".";
59static gunichar account_uc_separator = ':';
60
61static bool imap_convert_bayes_to_flat_run = false;
62
63/* Predefined KVP paths */
64static const std::string KEY_ASSOC_INCOME_ACCOUNT("ofx/associated-income-account");
65static const std::string KEY_RECONCILE_INFO("reconcile-info");
66static const std::string KEY_INCLUDE_CHILDREN("include-children");
67static const std::string KEY_POSTPONE("postpone");
68static const std::string KEY_LOT_MGMT("lot-mgmt");
69static const std::string KEY_ONLINE_ID("online_id");
70static const std::string KEY_IMP_APPEND_TEXT("import-append-text");
71static const std::string AB_KEY("hbci");
72static const std::string AB_ACCOUNT_ID("account-id");
73static const std::string AB_ACCOUNT_UID("account-uid");
74static const std::string AB_BANK_CODE("bank-code");
75static const std::string AB_TRANS_RETRIEVAL("trans-retrieval");
76
77static const std::string KEY_BALANCE_LIMIT("balance-limit");
78static const std::string KEY_BALANCE_HIGHER_LIMIT_VALUE("higher-value");
79static const std::string KEY_BALANCE_LOWER_LIMIT_VALUE("lower-value");
80static const std::string KEY_BALANCE_INCLUDE_SUB_ACCTS("inlude-sub-accts");
81
82using FinalProbabilityVec=std::vector<std::pair<std::string, int32_t>>;
83using ProbabilityVec=std::vector<std::pair<std::string, struct AccountProbability>>;
84using FlatKvpEntry=std::pair<std::string, KvpValue*>;
85
86enum
87{
88 LAST_SIGNAL
89};
90
91enum
92{
93 PROP_0,
94 PROP_NAME, /* Table */
95 PROP_FULL_NAME, /* Constructed */
96 PROP_CODE, /* Table */
97 PROP_DESCRIPTION, /* Table */
98 PROP_COLOR, /* KVP */
99 PROP_NOTES, /* KVP */
100 PROP_TYPE, /* Table */
101
102// PROP_PARENT, /* Table, Not a property */
103 PROP_COMMODITY, /* Table */
104 PROP_COMMODITY_SCU, /* Table */
105 PROP_NON_STD_SCU, /* Table */
106 PROP_END_BALANCE, /* Constructed */
107 PROP_END_NOCLOSING_BALANCE, /* Constructed */
108 PROP_END_CLEARED_BALANCE, /* Constructed */
109 PROP_END_RECONCILED_BALANCE, /* Constructed */
110
111 PROP_TAX_RELATED, /* KVP */
112 PROP_TAX_CODE, /* KVP */
113 PROP_TAX_SOURCE, /* KVP */
114 PROP_TAX_COPY_NUMBER, /* KVP */
115
116 PROP_HIDDEN, /* Table slot exists, but in KVP in memory & xml */
117 PROP_PLACEHOLDER, /* Table slot exists, but in KVP in memory & xml */
118 PROP_AUTO_INTEREST,
119 PROP_FILTER, /* KVP */
120 PROP_SORT_ORDER, /* KVP */
121 PROP_SORT_REVERSED,
122
123 PROP_LOT_NEXT_ID, /* KVP */
124 PROP_ONLINE_ACCOUNT, /* KVP */
125 PROP_IMP_APPEND_TEXT, /* KVP */
126 PROP_IS_OPENING_BALANCE, /* KVP */
127 PROP_OFX_INCOME_ACCOUNT, /* KVP */
128 PROP_AB_ACCOUNT_ID, /* KVP */
129 PROP_AB_ACCOUNT_UID, /* KVP */
130 PROP_AB_BANK_CODE, /* KVP */
131 PROP_AB_TRANS_RETRIEVAL, /* KVP */
132
133 PROP_RUNTIME_0,
134 PROP_POLICY, /* Cached Value */
135 PROP_MARK, /* Runtime Value */
136 PROP_SORT_DIRTY, /* Runtime Value */
137 PROP_BALANCE_DIRTY, /* Runtime Value */
138 PROP_START_BALANCE, /* Runtime Value */
139 PROP_START_NOCLOSING_BALANCE, /* Runtime Value */
140 PROP_START_CLEARED_BALANCE, /* Runtime Value */
141 PROP_START_RECONCILED_BALANCE, /* Runtime Value */
142};
143
144#define GET_PRIVATE(o) \
145 ((AccountPrivate*)gnc_account_get_instance_private((Account*)o))
146
147/* This map contains a set of strings representing the different column types. */
148static const std::map<GNCAccountType, const char*> gnc_acct_debit_strs = {
149 { ACCT_TYPE_NONE, N_("Funds In") },
150 { ACCT_TYPE_BANK, N_("Deposit") },
151 { ACCT_TYPE_CASH, N_("Receive") },
152 { ACCT_TYPE_CREDIT, N_("Payment") },
153 { ACCT_TYPE_ASSET, N_("Increase") },
154 { ACCT_TYPE_LIABILITY, N_("Decrease") },
155 { ACCT_TYPE_STOCK, N_("Buy") },
156 { ACCT_TYPE_MUTUAL, N_("Buy") },
157 { ACCT_TYPE_CURRENCY, N_("Buy") },
158 { ACCT_TYPE_INCOME, N_("Charge") },
159 { ACCT_TYPE_EXPENSE, N_("Expense") },
160 { ACCT_TYPE_PAYABLE, N_("Payment") },
161 { ACCT_TYPE_RECEIVABLE, N_("Invoice") },
162 { ACCT_TYPE_TRADING, N_("Decrease") },
163 { ACCT_TYPE_EQUITY, N_("Decrease") },
164};
165static const char* dflt_acct_debit_str = N_("Debit");
166
167/* This map contains a set of strings representing the different column types. */
168static const std::map<GNCAccountType, const char*> gnc_acct_credit_strs = {
169 { ACCT_TYPE_NONE, N_("Funds Out") },
170 { ACCT_TYPE_BANK, N_("Withdrawal") },
171 { ACCT_TYPE_CASH, N_("Spend") },
172 { ACCT_TYPE_CREDIT, N_("Charge") },
173 { ACCT_TYPE_ASSET, N_("Decrease") },
174 { ACCT_TYPE_LIABILITY, N_("Increase") },
175 { ACCT_TYPE_STOCK, N_("Sell") },
176 { ACCT_TYPE_MUTUAL, N_("Sell") },
177 { ACCT_TYPE_CURRENCY, N_("Sell") },
178 { ACCT_TYPE_INCOME, N_("Income") },
179 { ACCT_TYPE_EXPENSE, N_("Rebate") },
180 { ACCT_TYPE_PAYABLE, N_("Bill") },
181 { ACCT_TYPE_RECEIVABLE, N_("Payment") },
182 { ACCT_TYPE_TRADING, N_("Increase") },
183 { ACCT_TYPE_EQUITY, N_("Increase") },
184};
185static const char* dflt_acct_credit_str = N_("Credit");
186
187/********************************************************************\
188 * Because I can't use C++ for this project, doesn't mean that I *
189 * can't pretend to! These functions perform actions on the *
190 * account data structure, in order to encapsulate the knowledge *
191 * of the internals of the Account in one file. *
192\********************************************************************/
193
194static void xaccAccountBringUpToDate (Account *acc);
195
196
197/********************************************************************\
198 * gnc_get_account_separator *
199 * returns the current account separator character *
200 * *
201 * Args: none *
202 * Returns: account separator character *
203 \*******************************************************************/
204const gchar *
206{
207 return account_separator;
208}
209
210gunichar
211gnc_get_account_separator (void)
212{
213 return account_uc_separator;
214}
215
216void
217gnc_set_account_separator (const gchar *separator)
218{
219 gunichar uc;
220 gint count;
221
222 uc = g_utf8_get_char_validated(separator, -1);
223 if ((uc == (gunichar) - 2) || (uc == (gunichar) - 1) || g_unichar_isalnum(uc))
224 {
225 account_uc_separator = ':';
226 strcpy(account_separator, ":");
227 return;
228 }
229
230 account_uc_separator = uc;
231 count = g_unichar_to_utf8(uc, account_separator);
232 account_separator[count] = '\0';
233}
234
235gchar *gnc_account_name_violations_errmsg (const gchar *separator, GList* invalid_account_names)
236{
237 gchar *message = nullptr;
238
239 if ( !invalid_account_names )
240 return nullptr;
241
242 auto account_list {gnc_g_list_stringjoin (invalid_account_names, "\n")};
243
244 /* Translators: The first %s will be the account separator character,
245 the second %s is a list of account names.
246 The resulting string will be displayed to the user if there are
247 account names containing the separator character. */
248 message = g_strdup_printf(
249 _("The separator character \"%s\" is used in one or more account names.\n\n"
250 "This will result in unexpected behaviour. "
251 "Either change the account names or choose another separator character.\n\n"
252 "Below you will find the list of invalid account names:\n"
253 "%s"), separator, account_list );
254 g_free ( account_list );
255 return message;
256}
257
259{
260 GList *list;
261 const gchar *separator;
262};
263
264static void
265check_acct_name (Account *acct, gpointer user_data)
266{
267 auto cb {static_cast<ViolationData*>(user_data)};
268 auto name {xaccAccountGetName (acct)};
269 if (g_strstr_len (name, -1, cb->separator))
270 cb->list = g_list_prepend (cb->list, g_strdup (name));
271}
272
273GList *gnc_account_list_name_violations (QofBook *book, const gchar *separator)
274{
275 g_return_val_if_fail (separator != nullptr, nullptr);
276 if (!book) return nullptr;
277 ViolationData cb = { nullptr, separator };
278 gnc_account_foreach_descendant (gnc_book_get_root_account (book),
279 (AccountCb)check_acct_name, &cb);
280 return cb.list;
281}
282
283/********************************************************************\
284\********************************************************************/
285
286static inline void mark_account (Account *acc);
287void
288mark_account (Account *acc)
289{
290 qof_instance_set_dirty(&acc->inst);
291}
292
293/********************************************************************\
294\********************************************************************/
295
296/* GObject Initialization */
297G_DEFINE_TYPE_WITH_PRIVATE(Account, gnc_account, QOF_TYPE_INSTANCE)
298
299static void
300gnc_account_init(Account* acc)
301{
302 AccountPrivate *priv;
303
304 priv = GET_PRIVATE(acc);
305 priv->parent = nullptr;
306
307 priv->accountName = qof_string_cache_insert("");
308 priv->accountCode = qof_string_cache_insert("");
309 priv->description = qof_string_cache_insert("");
310
311 priv->type = ACCT_TYPE_NONE;
312
313 priv->mark = 0;
314
315 priv->policy = xaccGetFIFOPolicy();
316 priv->lots = nullptr;
317
318 priv->commodity = nullptr;
319 priv->commodity_scu = 0;
320 priv->non_standard_scu = FALSE;
321
322 priv->balance = gnc_numeric_zero();
323 priv->noclosing_balance = gnc_numeric_zero();
324 priv->cleared_balance = gnc_numeric_zero();
325 priv->reconciled_balance = gnc_numeric_zero();
326 priv->starting_balance = gnc_numeric_zero();
327 priv->starting_noclosing_balance = gnc_numeric_zero();
328 priv->starting_cleared_balance = gnc_numeric_zero();
329 priv->starting_reconciled_balance = gnc_numeric_zero();
330 priv->balance_dirty = FALSE;
331 priv->has_stock_split = false;
332
333 new (&priv->children) AccountVec ();
334 new (&priv->splits) SplitsVec ();
335 priv->splits_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
336 priv->sort_dirty = FALSE;
337}
338
339static void
340gnc_account_dispose (GObject *acctp)
341{
342 G_OBJECT_CLASS(gnc_account_parent_class)->dispose(acctp);
343}
344
345static void
346gnc_account_finalize(GObject* acctp)
347{
348 G_OBJECT_CLASS(gnc_account_parent_class)->finalize(acctp);
349}
350
351/* Note that g_value_set_object() refs the object, as does
352 * g_object_get(). But g_object_get() only unrefs once when it disgorges
353 * the object, leaving an unbalanced ref, which leaks. So instead of
354 * using g_value_set_object(), use g_value_take_object() which doesn't
355 * ref the object when used in get_property().
356 */
357static void
358gnc_account_get_property (GObject *object,
359 guint prop_id,
360 GValue *value,
361 GParamSpec *pspec)
362{
363 Account *account;
364 AccountPrivate *priv;
365
366 g_return_if_fail(GNC_IS_ACCOUNT(object));
367
368 account = GNC_ACCOUNT(object);
369 priv = GET_PRIVATE(account);
370 switch (prop_id)
371 {
372 case PROP_NAME:
373 g_value_set_string(value, priv->accountName);
374 break;
375 case PROP_FULL_NAME:
376 g_value_take_string(value, gnc_account_get_full_name(account));
377 break;
378 case PROP_CODE:
379 g_value_set_string(value, priv->accountCode);
380 break;
381 case PROP_DESCRIPTION:
382 g_value_set_string(value, priv->description);
383 break;
384 case PROP_COLOR:
385 g_value_set_string(value, xaccAccountGetColor(account));
386 break;
387 case PROP_NOTES:
388 g_value_set_string(value, xaccAccountGetNotes(account));
389 break;
390 case PROP_TYPE:
391 // NEED TO BE CONVERTED TO A G_TYPE_ENUM
392 g_value_set_int(value, priv->type);
393 break;
394 case PROP_COMMODITY:
395 g_value_take_object(value, priv->commodity);
396 break;
397 case PROP_COMMODITY_SCU:
398 g_value_set_int(value, priv->commodity_scu);
399 break;
400 case PROP_NON_STD_SCU:
401 g_value_set_boolean(value, priv->non_standard_scu);
402 break;
403 case PROP_SORT_DIRTY:
404 g_value_set_boolean(value, priv->sort_dirty);
405 break;
406 case PROP_BALANCE_DIRTY:
407 g_value_set_boolean(value, priv->balance_dirty);
408 break;
409 case PROP_START_BALANCE:
410 g_value_set_boxed(value, &priv->starting_balance);
411 break;
412 case PROP_START_NOCLOSING_BALANCE:
413 g_value_set_boxed(value, &priv->starting_noclosing_balance);
414 break;
415 case PROP_START_CLEARED_BALANCE:
416 g_value_set_boxed(value, &priv->starting_cleared_balance);
417 break;
418 case PROP_START_RECONCILED_BALANCE:
419 g_value_set_boxed(value, &priv->starting_reconciled_balance);
420 break;
421 case PROP_END_BALANCE:
422 g_value_set_boxed(value, &priv->balance);
423 break;
424 case PROP_END_NOCLOSING_BALANCE:
425 g_value_set_boxed(value, &priv->noclosing_balance);
426 break;
427 case PROP_END_CLEARED_BALANCE:
428 g_value_set_boxed(value, &priv->cleared_balance);
429 break;
430 case PROP_END_RECONCILED_BALANCE:
431 g_value_set_boxed(value, &priv->reconciled_balance);
432 break;
433 case PROP_POLICY:
434 /* MAKE THIS A BOXED VALUE */
435 g_value_set_pointer(value, priv->policy);
436 break;
437 case PROP_MARK:
438 g_value_set_int(value, priv->mark);
439 break;
440 case PROP_TAX_RELATED:
441 g_value_set_boolean(value, xaccAccountGetTaxRelated(account));
442 break;
443 case PROP_TAX_CODE:
444 g_value_set_string(value, xaccAccountGetTaxUSCode(account));
445 break;
446 case PROP_TAX_SOURCE:
447 g_value_set_string(value,
449 break;
450 case PROP_TAX_COPY_NUMBER:
451 g_value_set_int64(value,
453 break;
454 case PROP_HIDDEN:
455 g_value_set_boolean(value, xaccAccountGetHidden(account));
456 break;
457 case PROP_AUTO_INTEREST:
458 g_value_set_boolean (value, xaccAccountGetAutoInterest (account));
459 break;
460 case PROP_IS_OPENING_BALANCE:
461 g_value_set_boolean(value, xaccAccountGetIsOpeningBalance(account));
462 break;
463 case PROP_PLACEHOLDER:
464 g_value_set_boolean(value, xaccAccountGetPlaceholder(account));
465 break;
466 case PROP_FILTER:
467 g_value_set_string(value, xaccAccountGetFilter(account));
468 break;
469 case PROP_SORT_ORDER:
470 g_value_set_string(value, xaccAccountGetSortOrder(account));
471 break;
472 case PROP_SORT_REVERSED:
473 g_value_set_boolean(value, xaccAccountGetSortReversed(account));
474 break;
475 case PROP_LOT_NEXT_ID:
476 /* Pre-set the value in case the frame is empty */
477 g_value_set_int64 (value, 0);
478 qof_instance_get_path_kvp (QOF_INSTANCE (account), value, {KEY_LOT_MGMT, "next-id"});
479 break;
480 case PROP_ONLINE_ACCOUNT:
481 qof_instance_get_path_kvp (QOF_INSTANCE (account), value, {KEY_ONLINE_ID});
482 break;
483 case PROP_IMP_APPEND_TEXT:
484 g_value_set_boolean(value, xaccAccountGetAppendText(account));
485 break;
486 case PROP_OFX_INCOME_ACCOUNT:
487 qof_instance_get_path_kvp (QOF_INSTANCE (account), value, {KEY_ASSOC_INCOME_ACCOUNT});
488 break;
489 case PROP_AB_ACCOUNT_ID:
490 qof_instance_get_path_kvp (QOF_INSTANCE (account), value, {AB_KEY, AB_ACCOUNT_ID});
491 break;
492 case PROP_AB_ACCOUNT_UID:
493 qof_instance_get_path_kvp (QOF_INSTANCE (account), value, {AB_KEY, AB_ACCOUNT_UID});
494 break;
495 case PROP_AB_BANK_CODE:
496 qof_instance_get_path_kvp (QOF_INSTANCE (account), value, {AB_KEY, AB_BANK_CODE});
497 break;
498 case PROP_AB_TRANS_RETRIEVAL:
499 qof_instance_get_path_kvp (QOF_INSTANCE (account), value, {AB_KEY, AB_TRANS_RETRIEVAL});
500 break;
501 default:
502 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
503 break;
504 }
505}
506
507static void
508gnc_account_set_property (GObject *object,
509 guint prop_id,
510 const GValue *value,
511 GParamSpec *pspec)
512{
513 Account *account;
514 gnc_numeric *number;
515 g_return_if_fail(GNC_IS_ACCOUNT(object));
516 account = GNC_ACCOUNT(object);
517 if (prop_id < PROP_RUNTIME_0)
518 g_assert (qof_instance_get_editlevel(account));
519
520 switch (prop_id)
521 {
522 case PROP_NAME:
523 xaccAccountSetName(account, g_value_get_string(value));
524 break;
525 case PROP_CODE:
526 xaccAccountSetCode(account, g_value_get_string(value));
527 break;
528 case PROP_DESCRIPTION:
529 xaccAccountSetDescription(account, g_value_get_string(value));
530 break;
531 case PROP_COLOR:
532 xaccAccountSetColor(account, g_value_get_string(value));
533 break;
534 case PROP_NOTES:
535 xaccAccountSetNotes(account, g_value_get_string(value));
536 break;
537 case PROP_TYPE:
538 // NEED TO BE CONVERTED TO A G_TYPE_ENUM
539 xaccAccountSetType(account, static_cast<GNCAccountType>(g_value_get_int(value)));
540 break;
541 case PROP_COMMODITY:
542 xaccAccountSetCommodity(account, static_cast<gnc_commodity*>(g_value_get_object(value)));
543 break;
544 case PROP_COMMODITY_SCU:
545 xaccAccountSetCommoditySCU(account, g_value_get_int(value));
546 break;
547 case PROP_NON_STD_SCU:
548 xaccAccountSetNonStdSCU(account, g_value_get_boolean(value));
549 break;
550 case PROP_SORT_DIRTY:
552 break;
553 case PROP_BALANCE_DIRTY:
555 break;
556 case PROP_START_BALANCE:
557 number = static_cast<gnc_numeric*>(g_value_get_boxed(value));
558 gnc_account_set_start_balance(account, *number);
559 break;
560 case PROP_START_CLEARED_BALANCE:
561 number = static_cast<gnc_numeric*>(g_value_get_boxed(value));
563 break;
564 case PROP_START_RECONCILED_BALANCE:
565 number = static_cast<gnc_numeric*>(g_value_get_boxed(value));
567 break;
568 case PROP_POLICY:
569 gnc_account_set_policy(account, static_cast<GNCPolicy*>(g_value_get_pointer(value)));
570 break;
571 case PROP_MARK:
572 xaccAccountSetMark(account, g_value_get_int(value));
573 break;
574 case PROP_TAX_RELATED:
575 xaccAccountSetTaxRelated(account, g_value_get_boolean(value));
576 break;
577 case PROP_TAX_CODE:
578 xaccAccountSetTaxUSCode(account, g_value_get_string(value));
579 break;
580 case PROP_TAX_SOURCE:
582 g_value_get_string(value));
583 break;
584 case PROP_TAX_COPY_NUMBER:
586 g_value_get_int64(value));
587 break;
588 case PROP_HIDDEN:
589 xaccAccountSetHidden(account, g_value_get_boolean(value));
590 break;
591 case PROP_AUTO_INTEREST:
592 xaccAccountSetAutoInterest (account, g_value_get_boolean (value));
593 break;
594 case PROP_IS_OPENING_BALANCE:
595 xaccAccountSetIsOpeningBalance (account, g_value_get_boolean (value));
596 break;
597 case PROP_PLACEHOLDER:
598 xaccAccountSetPlaceholder(account, g_value_get_boolean(value));
599 break;
600 case PROP_FILTER:
601 xaccAccountSetFilter(account, g_value_get_string(value));
602 break;
603 case PROP_SORT_ORDER:
604 xaccAccountSetSortOrder(account, g_value_get_string(value));
605 break;
606 case PROP_SORT_REVERSED:
607 xaccAccountSetSortReversed(account, g_value_get_boolean(value));
608 break;
609 case PROP_LOT_NEXT_ID:
610 qof_instance_set_path_kvp (QOF_INSTANCE (account), value, {KEY_LOT_MGMT, "next-id"});
611 break;
612 case PROP_ONLINE_ACCOUNT:
613 qof_instance_set_path_kvp (QOF_INSTANCE (account), value, {KEY_ONLINE_ID});
614 break;
615 case PROP_IMP_APPEND_TEXT:
616 xaccAccountSetAppendText(account, g_value_get_boolean(value));
617 break;
618 case PROP_OFX_INCOME_ACCOUNT:
619 qof_instance_set_path_kvp (QOF_INSTANCE (account), value, {KEY_ASSOC_INCOME_ACCOUNT});
620 break;
621 case PROP_AB_ACCOUNT_ID:
622 qof_instance_set_path_kvp (QOF_INSTANCE (account), value, {AB_KEY, AB_ACCOUNT_ID});
623 break;
624 case PROP_AB_ACCOUNT_UID:
625 qof_instance_set_path_kvp (QOF_INSTANCE (account), value, {AB_KEY, AB_ACCOUNT_UID});
626 break;
627 case PROP_AB_BANK_CODE:
628 qof_instance_set_path_kvp (QOF_INSTANCE (account), value, {AB_KEY, AB_BANK_CODE});
629 break;
630 case PROP_AB_TRANS_RETRIEVAL:
631 qof_instance_set_path_kvp (QOF_INSTANCE (account), value, {AB_KEY, AB_TRANS_RETRIEVAL});
632 break;
633 default:
634 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
635 break;
636 }
637}
638
639static void
640gnc_account_class_init (AccountClass *klass)
641{
642 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
643
644 gobject_class->dispose = gnc_account_dispose;
645 gobject_class->finalize = gnc_account_finalize;
646 gobject_class->set_property = gnc_account_set_property;
647 gobject_class->get_property = gnc_account_get_property;
648
649 g_object_class_install_property
650 (gobject_class,
651 PROP_NAME,
652 g_param_spec_string ("name",
653 "Account Name",
654 "The accountName is an arbitrary string "
655 "assigned by the user. It is intended to "
656 "a short, 5 to 30 character long string "
657 "that is displayed by the GUI as the "
658 "account mnemonic. Account names may be "
659 "repeated. but no two accounts that share "
660 "a parent may have the same name.",
661 nullptr,
662 static_cast<GParamFlags>(G_PARAM_READWRITE)));
663
664 g_object_class_install_property
665 (gobject_class,
666 PROP_FULL_NAME,
667 g_param_spec_string ("fullname",
668 "Full Account Name",
669 "The name of the account concatenated with "
670 "all its parent account names to indicate "
671 "a unique account.",
672 nullptr,
673 static_cast<GParamFlags>(G_PARAM_READABLE)));
674
675 g_object_class_install_property
676 (gobject_class,
677 PROP_CODE,
678 g_param_spec_string ("code",
679 "Account Code",
680 "The account code is an arbitrary string "
681 "assigned by the user. It is intended to "
682 "be reporting code that is a synonym for "
683 "the accountName.",
684 nullptr,
685 static_cast<GParamFlags>(G_PARAM_READWRITE)));
686
687 g_object_class_install_property
688 (gobject_class,
689 PROP_DESCRIPTION,
690 g_param_spec_string ("description",
691 "Account Description",
692 "The account description is an arbitrary "
693 "string assigned by the user. It is intended "
694 "to be a longer, 1-5 sentence description of "
695 "what this account is all about.",
696 nullptr,
697 static_cast<GParamFlags>(G_PARAM_READWRITE)));
698
699 g_object_class_install_property
700 (gobject_class,
701 PROP_COLOR,
702 g_param_spec_string ("color",
703 "Account Color",
704 "The account color is a color string assigned "
705 "by the user. It is intended to highlight the "
706 "account based on the users wishes.",
707 nullptr,
708 static_cast<GParamFlags>(G_PARAM_READWRITE)));
709
710 g_object_class_install_property
711 (gobject_class,
712 PROP_NOTES,
713 g_param_spec_string ("notes",
714 "Account Notes",
715 "The account notes is an arbitrary provided "
716 "for the user to attach any other text that "
717 "they would like to associate with the account.",
718 nullptr,
719 static_cast<GParamFlags>(G_PARAM_READWRITE)));
720
721 g_object_class_install_property
722 (gobject_class,
723 PROP_TYPE,
724 g_param_spec_int ("type",
725 "Account Type",
726 "The account type, picked from the enumerated list "
727 "that includes ACCT_TYPE_BANK, ACCT_TYPE_STOCK, "
728 "ACCT_TYPE_CREDIT, ACCT_TYPE_INCOME, etc.",
731 ACCT_TYPE_BANK,
732 static_cast<GParamFlags>(G_PARAM_READWRITE)));
733
734 g_object_class_install_property
735 (gobject_class,
736 PROP_COMMODITY,
737 g_param_spec_object ("commodity",
738 "Commodity",
739 "The commodity field denotes the kind of "
740 "'stuff' stored in this account, whether "
741 "it is USD, gold, stock, etc.",
742 GNC_TYPE_COMMODITY,
743 static_cast<GParamFlags>(G_PARAM_READWRITE)));
744
745 g_object_class_install_property
746 (gobject_class,
747 PROP_COMMODITY_SCU,
748 g_param_spec_int ("commodity-scu",
749 "Commodity SCU",
750 "The smallest fraction of the commodity that is "
751 "tracked. This number is used as the denominator "
752 "value in 1/x, so a value of 100 says that the "
753 "commodity can be divided into hundredths. E.G."
754 "1 USD can be divided into 100 cents.",
755 0,
756 G_MAXINT32,
758 static_cast<GParamFlags>(G_PARAM_READWRITE)));
759
760 g_object_class_install_property
761 (gobject_class,
762 PROP_NON_STD_SCU,
763 g_param_spec_boolean ("non-std-scu",
764 "Non-std SCU",
765 "TRUE if the account SCU doesn't match "
766 "the commodity SCU. This indicates a case "
767 "where the two were accidentally set to "
768 "mismatched values in older versions of "
769 "GnuCash.",
770 FALSE,
771 static_cast<GParamFlags>(G_PARAM_READWRITE)));
772
773 g_object_class_install_property
774 (gobject_class,
775 PROP_SORT_DIRTY,
776 g_param_spec_boolean("sort-dirty",
777 "Sort Dirty",
778 "TRUE if the splits in the account needs to be "
779 "resorted. This flag is set by the accounts "
780 "code for certain internal modifications, or "
781 "when external code calls the engine to say a "
782 "split has been modified in a way that may "
783 "affect the sort order of the account. Note: "
784 "This value can only be set to TRUE.",
785 FALSE,
786 static_cast<GParamFlags>(G_PARAM_READWRITE)));
787
788 g_object_class_install_property
789 (gobject_class,
790 PROP_BALANCE_DIRTY,
791 g_param_spec_boolean("balance-dirty",
792 "Balance Dirty",
793 "TRUE if the running balances in the account "
794 "needs to be recalculated. This flag is set "
795 "by the accounts code for certain internal "
796 "modifications, or when external code calls "
797 "the engine to say a split has been modified. "
798 "Note: This value can only be set to TRUE.",
799 FALSE,
800 static_cast<GParamFlags>(G_PARAM_READWRITE)));
801
802 g_object_class_install_property
803 (gobject_class,
804 PROP_START_BALANCE,
805 g_param_spec_boxed("start-balance",
806 "Starting Balance",
807 "The starting balance for the account. This "
808 "parameter is intended for use with backends that "
809 "do not return the complete list of splits for an "
810 "account, but rather return a partial list. In "
811 "such a case, the backend will typically return "
812 "all of the splits after some certain date, and "
813 "the 'starting balance' will represent the "
814 "summation of the splits up to that date.",
815 GNC_TYPE_NUMERIC,
816 static_cast<GParamFlags>(G_PARAM_READWRITE)));
817
818 g_object_class_install_property
819 (gobject_class,
820 PROP_START_NOCLOSING_BALANCE,
821 g_param_spec_boxed("start-noclosing-balance",
822 "Starting No-closing Balance",
823 "The starting balance for the account, ignoring closing."
824 "This parameter is intended for use with backends "
825 "that do not return the complete list of splits "
826 "for an account, but rather return a partial "
827 "list. In such a case, the backend will "
828 "typically return all of the splits after "
829 "some certain date, and the 'starting noclosing "
830 "balance' will represent the summation of the "
831 "splits up to that date, ignoring closing splits.",
832 GNC_TYPE_NUMERIC,
833 static_cast<GParamFlags>(G_PARAM_READWRITE)));
834
835 g_object_class_install_property
836 (gobject_class,
837 PROP_START_CLEARED_BALANCE,
838 g_param_spec_boxed("start-cleared-balance",
839 "Starting Cleared Balance",
840 "The starting cleared balance for the account. "
841 "This parameter is intended for use with backends "
842 "that do not return the complete list of splits "
843 "for an account, but rather return a partial "
844 "list. In such a case, the backend will "
845 "typically return all of the splits after "
846 "some certain date, and the 'starting cleared "
847 "balance' will represent the summation of the "
848 "splits up to that date.",
849 GNC_TYPE_NUMERIC,
850 static_cast<GParamFlags>(G_PARAM_READWRITE)));
851
852 g_object_class_install_property
853 (gobject_class,
854 PROP_START_RECONCILED_BALANCE,
855 g_param_spec_boxed("start-reconciled-balance",
856 "Starting Reconciled Balance",
857 "The starting reconciled balance for the "
858 "account. This parameter is intended for use "
859 "with backends that do not return the complete "
860 "list of splits for an account, but rather return "
861 "a partial list. In such a case, the backend "
862 "will typically return all of the splits after "
863 "some certain date, and the 'starting reconciled "
864 "balance' will represent the summation of the "
865 "splits up to that date.",
866 GNC_TYPE_NUMERIC,
867 static_cast<GParamFlags>(G_PARAM_READWRITE)));
868
869 g_object_class_install_property
870 (gobject_class,
871 PROP_END_BALANCE,
872 g_param_spec_boxed("end-balance",
873 "Ending Account Balance",
874 "This is the current ending balance for the "
875 "account. It is computed from the sum of the "
876 "starting balance and all splits in the account.",
877 GNC_TYPE_NUMERIC,
878 G_PARAM_READABLE));
879
880 g_object_class_install_property
881 (gobject_class,
882 PROP_END_NOCLOSING_BALANCE,
883 g_param_spec_boxed("end-noclosing-balance",
884 "Ending Account Noclosing Balance",
885 "This is the current ending no-closing balance for "
886 "the account. It is computed from the sum of the "
887 "starting balance and all cleared splits in the "
888 "account.",
889 GNC_TYPE_NUMERIC,
890 G_PARAM_READABLE));
891
892 g_object_class_install_property
893 (gobject_class,
894 PROP_END_CLEARED_BALANCE,
895 g_param_spec_boxed("end-cleared-balance",
896 "Ending Account Cleared Balance",
897 "This is the current ending cleared balance for "
898 "the account. It is computed from the sum of the "
899 "starting balance and all cleared splits in the "
900 "account.",
901 GNC_TYPE_NUMERIC,
902 G_PARAM_READABLE));
903
904 g_object_class_install_property
905 (gobject_class,
906 PROP_END_RECONCILED_BALANCE,
907 g_param_spec_boxed("end-reconciled-balance",
908 "Ending Account Reconciled Balance",
909 "This is the current ending reconciled balance "
910 "for the account. It is computed from the sum of "
911 "the starting balance and all reconciled splits "
912 "in the account.",
913 GNC_TYPE_NUMERIC,
914 static_cast<GParamFlags>(G_PARAM_READABLE)));
915
916 g_object_class_install_property
917 (gobject_class,
918 PROP_POLICY,
919 g_param_spec_pointer ("policy",
920 "Policy",
921 "The account lots policy.",
922 static_cast<GParamFlags>(G_PARAM_READWRITE)));
923
924 g_object_class_install_property
925 (gobject_class,
926 PROP_MARK,
927 g_param_spec_int ("acct-mark",
928 "Account Mark",
929 "Ipsum Lorem",
930 0,
931 G_MAXINT16,
932 0,
933 static_cast<GParamFlags>(G_PARAM_READWRITE)));
934
935 g_object_class_install_property
936 (gobject_class,
937 PROP_TAX_RELATED,
938 g_param_spec_boolean ("tax-related",
939 "Tax Related",
940 "Whether the account maps to an entry on an "
941 "income tax document.",
942 FALSE,
943 static_cast<GParamFlags>(G_PARAM_READWRITE)));
944
945 g_object_class_install_property
946 (gobject_class,
947 PROP_IS_OPENING_BALANCE,
948 g_param_spec_boolean ("opening-balance",
949 "Opening Balance",
950 "Whether the account holds opening balances",
951 FALSE,
952 static_cast<GParamFlags>(G_PARAM_READWRITE)));
953
954 g_object_class_install_property
955 (gobject_class,
956 PROP_TAX_CODE,
957 g_param_spec_string ("tax-code",
958 "Tax Code",
959 "This is the code for mapping an account to a "
960 "specific entry on a taxable document. In the "
961 "United States it is used to transfer totals "
962 "into tax preparation software.",
963 nullptr,
964 static_cast<GParamFlags>(G_PARAM_READWRITE)));
965
966 g_object_class_install_property
967 (gobject_class,
968 PROP_TAX_SOURCE,
969 g_param_spec_string ("tax-source",
970 "Tax Source",
971 "This specifies where exported name comes from.",
972 nullptr,
973 static_cast<GParamFlags>(G_PARAM_READWRITE)));
974
975 g_object_class_install_property
976 (gobject_class,
977 PROP_TAX_COPY_NUMBER,
978 g_param_spec_int64 ("tax-copy-number",
979 "Tax Copy Number",
980 "This specifies the copy number of the tax "
981 "form/schedule.",
982 (gint64)1,
983 G_MAXINT64,
984 (gint64)1,
985 static_cast<GParamFlags>(G_PARAM_READWRITE)));
986
987 g_object_class_install_property
988 (gobject_class,
989 PROP_HIDDEN,
990 g_param_spec_boolean ("hidden",
991 "Hidden",
992 "Whether the account should be hidden in the "
993 "account tree.",
994 FALSE,
995 static_cast<GParamFlags>(G_PARAM_READWRITE)));
996
997 g_object_class_install_property
998 (gobject_class,
999 PROP_AUTO_INTEREST,
1000 g_param_spec_boolean ("auto-interest-transfer",
1001 "Auto Interest",
1002 "Whether an interest transfer should be automatically "
1003 "added before reconcile.",
1004 FALSE,
1005 static_cast<GParamFlags>(G_PARAM_READWRITE)));
1006
1007 g_object_class_install_property
1008 (gobject_class,
1009 PROP_PLACEHOLDER,
1010 g_param_spec_boolean ("placeholder",
1011 "Placeholder",
1012 "Whether the account is a placeholder account which does not "
1013 "allow transactions to be created, edited or deleted.",
1014 FALSE,
1015 static_cast<GParamFlags>(G_PARAM_READWRITE)));
1016
1017 g_object_class_install_property
1018 (gobject_class,
1019 PROP_FILTER,
1020 g_param_spec_string ("filter",
1021 "Account Filter",
1022 "The account filter is a value saved to allow "
1023 "filters to be recalled.",
1024 nullptr,
1025 static_cast<GParamFlags>(G_PARAM_READWRITE)));
1026
1027 g_object_class_install_property
1028 (gobject_class,
1029 PROP_SORT_ORDER,
1030 g_param_spec_string ("sort-order",
1031 "Account Sort Order",
1032 "The account sort order is a value saved to allow "
1033 "the sort order to be recalled.",
1034 nullptr,
1035 static_cast<GParamFlags>(G_PARAM_READWRITE)));
1036
1037 g_object_class_install_property
1038 (gobject_class,
1039 PROP_SORT_REVERSED,
1040 g_param_spec_boolean ("sort-reversed",
1041 "Account Sort Reversed",
1042 "Parameter to store whether the sort order is reversed or not.",
1043 FALSE,
1044 static_cast<GParamFlags>(G_PARAM_READWRITE)));
1045
1046 g_object_class_install_property
1047 (gobject_class,
1048 PROP_LOT_NEXT_ID,
1049 g_param_spec_int64 ("lot-next-id",
1050 "Lot Next ID",
1051 "Tracks the next id to use in gnc_lot_make_default.",
1052 (gint64)1,
1053 G_MAXINT64,
1054 (gint64)1,
1055 static_cast<GParamFlags>(G_PARAM_READWRITE)));
1056
1057 g_object_class_install_property
1058 (gobject_class,
1059 PROP_ONLINE_ACCOUNT,
1060 g_param_spec_string ("online-id",
1061 "Online Account ID",
1062 "The online account which corresponds to this "
1063 "account for OFX import",
1064 nullptr,
1065 static_cast<GParamFlags>(G_PARAM_READWRITE)));
1066
1067 g_object_class_install_property
1068 (gobject_class,
1069 PROP_IMP_APPEND_TEXT,
1070 g_param_spec_boolean ("import-append-text",
1071 "Import Append Text",
1072 "Saved state of Append checkbox for setting initial "
1073 "value next time this account is imported.",
1074 FALSE,
1075 static_cast<GParamFlags>(G_PARAM_READWRITE)));
1076
1077 g_object_class_install_property(
1078 gobject_class,
1079 PROP_OFX_INCOME_ACCOUNT,
1080 g_param_spec_boxed("ofx-income-account",
1081 "Associated income account",
1082 "Used by the OFX importer.",
1083 GNC_TYPE_GUID,
1084 static_cast<GParamFlags>(G_PARAM_READWRITE)));
1085
1086 g_object_class_install_property
1087 (gobject_class,
1088 PROP_AB_ACCOUNT_ID,
1089 g_param_spec_string ("ab-account-id",
1090 "AQBanking Account ID",
1091 "The AqBanking account which corresponds to this "
1092 "account for AQBanking import",
1093 nullptr,
1094 static_cast<GParamFlags>(G_PARAM_READWRITE)));
1095 g_object_class_install_property
1096 (gobject_class,
1097 PROP_AB_BANK_CODE,
1098 g_param_spec_string ("ab-bank-code",
1099 "AQBanking Bank Code",
1100 "The online account which corresponds to this "
1101 "account for AQBanking import",
1102 nullptr,
1103 static_cast<GParamFlags>(G_PARAM_READWRITE)));
1104
1105 g_object_class_install_property
1106 (gobject_class,
1107 PROP_AB_ACCOUNT_UID,
1108 g_param_spec_int64 ("ab-account-uid",
1109 "AQBanking Account UID",
1110 "Tracks the next id to use in gnc_lot_make_default.",
1111 (gint64)1,
1112 G_MAXINT64,
1113 (gint64)1,
1114 static_cast<GParamFlags>(G_PARAM_READWRITE)));
1115
1116 g_object_class_install_property
1117 (gobject_class,
1118 PROP_AB_TRANS_RETRIEVAL,
1119 g_param_spec_boxed("ab-trans-retrieval",
1120 "AQBanking Last Transaction Retrieval",
1121 "The time of the last transaction retrieval for this "
1122 "account.",
1123 GNC_TYPE_TIME64,
1124 static_cast<GParamFlags>(G_PARAM_READWRITE)));
1125
1126}
1127
1128static void
1129xaccInitAccount (Account * acc, QofBook *book)
1130{
1131 ENTER ("book=%p\n", book);
1132 qof_instance_init_data (&acc->inst, GNC_ID_ACCOUNT, book);
1133
1134 LEAVE ("account=%p\n", acc);
1135}
1136
1137/********************************************************************\
1138\********************************************************************/
1139
1140void
1141gnc_account_foreach_split (const Account *acc, std::function<void(Split*)> func)
1142{
1143 gnc_account_foreach_split_between_dates (acc, {}, {}, false, func);
1144}
1145
1146void
1147gnc_account_foreach_split_until_date (const Account *acc, time64 end_date,
1148 std::function<void(Split*)> f)
1149{
1150 gnc_account_foreach_split_between_dates (acc, {}, end_date, false, f);
1151}
1152
1153
1154Split*
1155gnc_account_find_split (const Account *acc, std::function<bool(const Split*)> predicate,
1156 bool reverse)
1157{
1158 if (!GNC_IS_ACCOUNT (acc))
1159 return nullptr;
1160
1161 const auto& splits{GET_PRIVATE(acc)->splits};
1162 if (reverse)
1163 {
1164 auto latest = std::find_if(splits.rbegin(), splits.rend(), predicate);
1165 return (latest == splits.rend()) ? nullptr : *latest;
1166 }
1167 else
1168 {
1169 auto earliest = std::find_if(splits.begin(), splits.end(), predicate);
1170 return (earliest == splits.end()) ? nullptr : *earliest;
1171 }
1172}
1173
1174/********************************************************************\
1175\********************************************************************/
1176
1177QofBook *
1178gnc_account_get_book(const Account *account)
1179{
1180 if (!account) return nullptr;
1181 return qof_instance_get_book(QOF_INSTANCE(account));
1182}
1183
1184/********************************************************************\
1185\********************************************************************/
1186
1187static Account *
1188gnc_coll_get_root_account (QofCollection *col)
1189{
1190 if (!col) return nullptr;
1191 return static_cast<Account*>(qof_collection_get_data (col));
1192}
1193
1194static void
1195gnc_coll_set_root_account (QofCollection *col, Account *root)
1196{
1197 AccountPrivate *rpriv;
1198 Account *old_root;
1199 if (!col) return;
1200
1201 old_root = gnc_coll_get_root_account (col);
1202 if (old_root == root) return;
1203
1204 /* If the new root is already linked into the tree somewhere, then
1205 * remove it from its current position before adding it at the
1206 * top. */
1207 rpriv = GET_PRIVATE(root);
1208 if (rpriv->parent)
1209 {
1211 gnc_account_remove_child(rpriv->parent, root);
1213 }
1214
1215 qof_collection_set_data (col, root);
1216
1217 if (old_root)
1218 {
1219 xaccAccountBeginEdit (old_root);
1220 xaccAccountDestroy (old_root);
1221 }
1222}
1223
1224Account *
1225gnc_book_get_root_account (QofBook *book)
1226{
1227 QofCollection *col;
1228 Account *root;
1229
1230 if (!book) return nullptr;
1231 col = qof_book_get_collection (book, GNC_ID_ROOT_ACCOUNT);
1232 root = gnc_coll_get_root_account (col);
1233 if (root == nullptr && !qof_book_shutting_down(book))
1234 root = gnc_account_create_root(book);
1235 return root;
1236}
1237
1238void
1239gnc_book_set_root_account (QofBook *book, Account *root)
1240{
1241 QofCollection *col;
1242 if (!book) return;
1243
1244 if (root && gnc_account_get_book(root) != book)
1245 {
1246 PERR ("cannot mix and match books freely!");
1247 return;
1248 }
1249
1250 col = qof_book_get_collection (book, GNC_ID_ROOT_ACCOUNT);
1251 gnc_coll_set_root_account (col, root);
1252}
1253
1254/********************************************************************\
1255\********************************************************************/
1256
1257Account *
1259{
1260 Account *acc;
1261
1262 g_return_val_if_fail (book, nullptr);
1263
1264 acc = static_cast<Account*>(g_object_new (GNC_TYPE_ACCOUNT, nullptr));
1265 xaccInitAccount (acc, book);
1266 qof_event_gen (&acc->inst, QOF_EVENT_CREATE, nullptr);
1267
1268 return acc;
1269}
1270
1271Account *
1273{
1274 Account *root;
1275 AccountPrivate *rpriv;
1276
1277 root = xaccMallocAccount(book);
1278 rpriv = GET_PRIVATE(root);
1280 rpriv->type = ACCT_TYPE_ROOT;
1281 rpriv->accountName = qof_string_cache_replace(rpriv->accountName, "Root Account");
1282 mark_account (root);
1284 gnc_book_set_root_account(book, root);
1285 return root;
1286}
1287
1288Account *
1290{
1291 Account *ret;
1292 AccountPrivate *from_priv, *priv;
1293
1294 g_return_val_if_fail(GNC_IS_ACCOUNT(from), nullptr);
1295 g_return_val_if_fail(QOF_IS_BOOK(book), nullptr);
1296
1297 ENTER (" ");
1298 ret = static_cast<Account*>(g_object_new (GNC_TYPE_ACCOUNT, nullptr));
1299 g_return_val_if_fail (ret, nullptr);
1300
1301 from_priv = GET_PRIVATE(from);
1302 priv = GET_PRIVATE(ret);
1303 xaccInitAccount (ret, book);
1304
1305 /* Do not Begin/CommitEdit() here; give the caller
1306 * a chance to fix things up, and let them do it.
1307 * Also let caller issue the generate_event (EVENT_CREATE) */
1308 priv->type = from_priv->type;
1309
1310 priv->accountName = qof_string_cache_replace(priv->accountName, from_priv->accountName);
1311 priv->accountCode = qof_string_cache_replace(priv->accountCode, from_priv->accountCode);
1312 priv->description = qof_string_cache_replace(priv->description, from_priv->description);
1313
1314 qof_instance_copy_kvp (QOF_INSTANCE (ret), QOF_INSTANCE (from));
1315
1316 /* The new book should contain a commodity that matches
1317 * the one in the old book. Find it, use it. */
1318 priv->commodity = gnc_commodity_obtain_twin(from_priv->commodity, book);
1319 gnc_commodity_increment_usage_count(priv->commodity);
1320
1321 priv->commodity_scu = from_priv->commodity_scu;
1322 priv->non_standard_scu = from_priv->non_standard_scu;
1323
1324 qof_instance_set_dirty(&ret->inst);
1325 LEAVE (" ");
1326 return ret;
1327}
1328
1329/********************************************************************\
1330\********************************************************************/
1331
1332static void
1333xaccFreeOneChildAccount (Account *acc)
1334{
1335 /* FIXME: this code is kind of hacky. actually, all this code
1336 * seems to assume that the account edit levels are all 1. */
1337 if (qof_instance_get_editlevel(acc) == 0)
1339 xaccAccountDestroy(acc);
1340}
1341
1342static void
1343xaccFreeAccountChildren (Account *acc)
1344{
1345 auto priv{GET_PRIVATE(acc)};
1346 /* Copy the list since it will be modified */
1347 auto children = priv->children;
1348 std::for_each (children.begin(), children.end(), xaccFreeOneChildAccount);
1349
1350 /* The foreach should have removed all the children already. */
1351 priv->children.clear();
1352}
1353
1354/* The xaccFreeAccount() routine releases memory associated with the
1355 * account. It should never be called directly from user code;
1356 * instead, the xaccAccountDestroy() routine should be used (because
1357 * xaccAccountDestroy() has the correct commit semantics). */
1358static void
1359xaccFreeAccount (Account *acc)
1360{
1361 AccountPrivate *priv;
1362 GList *lp;
1363
1364 g_return_if_fail(GNC_IS_ACCOUNT(acc));
1365
1366 priv = GET_PRIVATE(acc);
1367 qof_event_gen (&acc->inst, QOF_EVENT_DESTROY, nullptr);
1368
1369 /* Otherwise the lists below get munged while we're iterating
1370 * them, possibly crashing.
1371 */
1372 if (!qof_instance_get_destroying (acc))
1373 qof_instance_set_destroying(acc, TRUE);
1374
1375 if (!priv->children.empty())
1376 {
1377 PERR (" instead of calling xaccFreeAccount(), please call\n"
1378 " xaccAccountBeginEdit(); xaccAccountDestroy();\n");
1379
1380 /* First, recursively free children, also frees list */
1381 xaccFreeAccountChildren(acc);
1382 }
1383
1384 /* remove lots -- although these should be gone by now. */
1385 if (priv->lots)
1386 {
1387 PERR (" instead of calling xaccFreeAccount(), please call\n"
1388 " xaccAccountBeginEdit(); xaccAccountDestroy();\n");
1389
1390 for (lp = priv->lots; lp; lp = lp->next)
1391 {
1392 GNCLot *lot = static_cast<GNCLot*>(lp->data);
1393 gnc_lot_destroy (lot);
1394 }
1395 g_list_free (priv->lots);
1396 priv->lots = nullptr;
1397 }
1398
1399 /* Next, clean up the splits */
1400 /* NB there shouldn't be any splits by now ... they should
1401 * have been all been freed by CommitEdit(). We can remove this
1402 * check once we know the warning isn't occurring any more. */
1403 if (!priv->splits.empty())
1404 {
1405 PERR (" instead of calling xaccFreeAccount(), please call\n"
1406 " xaccAccountBeginEdit(); xaccAccountDestroy();\n");
1407
1408 qof_instance_reset_editlevel(acc);
1409
1410 for (auto s : priv->splits)
1411 {
1412 g_assert(xaccSplitGetAccount(s) == acc);
1413 xaccSplitDestroy (s);
1414 }
1415/* Nothing here (or in xaccAccountCommitEdit) nullptrs priv->splits, so this asserts every time.
1416 g_assert(priv->splits == nullptr);
1417*/
1418 }
1419
1420 qof_string_cache_remove(priv->accountName);
1421 qof_string_cache_remove(priv->accountCode);
1422 qof_string_cache_remove(priv->description);
1423 priv->accountName = priv->accountCode = priv->description = nullptr;
1424
1425 /* zero out values, just in case stray
1426 * pointers are pointing here. */
1427
1428 priv->last_num = nullptr;
1429 priv->tax_us_code = nullptr;
1430 priv->tax_us_pns = nullptr;
1431 priv->color = nullptr;
1432 priv->sort_order = nullptr;
1433 priv->notes = nullptr;
1434 priv->filter = nullptr;
1435
1436 priv->parent = nullptr;
1437
1438 priv->balance = gnc_numeric_zero();
1439 priv->noclosing_balance = gnc_numeric_zero();
1440 priv->cleared_balance = gnc_numeric_zero();
1441 priv->reconciled_balance = gnc_numeric_zero();
1442
1443 priv->type = ACCT_TYPE_NONE;
1444 gnc_commodity_decrement_usage_count(priv->commodity);
1445 priv->commodity = nullptr;
1446
1447 priv->balance_dirty = FALSE;
1448 priv->has_stock_split = false;
1449 priv->sort_dirty = FALSE;
1450 priv->splits.~SplitsVec();
1451 priv->children.~AccountVec();
1452 g_hash_table_destroy (priv->splits_hash);
1453
1454 /* qof_instance_release (&acc->inst); */
1455 g_object_unref(acc);
1456}
1457
1458/********************************************************************\
1459 * transactional routines
1460\********************************************************************/
1461
1462void
1464{
1465 g_return_if_fail(acc);
1466 qof_begin_edit(&acc->inst);
1467}
1468
1469static void on_done(QofInstance *inst)
1470{
1471 /* old event style */
1472 qof_event_gen (inst, QOF_EVENT_MODIFY, nullptr);
1473}
1474
1475static void on_err (QofInstance *inst, QofBackendError errcode)
1476{
1477 PERR("commit error: %d", errcode);
1478 gnc_engine_signal_commit_error( errcode );
1479}
1480
1481static void acc_free (QofInstance *inst)
1482{
1483 AccountPrivate *priv;
1484 Account *acc = (Account *) inst;
1485
1486 priv = GET_PRIVATE(acc);
1487 if (priv->parent)
1488 gnc_account_remove_child(priv->parent, acc);
1489 xaccFreeAccount(acc);
1490}
1491
1492static void
1493destroy_pending_splits_for_account(QofInstance *ent, gpointer acc)
1494{
1495 Transaction *trans = (Transaction *) ent;
1496 Split *split;
1497
1498 if (xaccTransIsOpen(trans))
1499 while ((split = xaccTransFindSplitByAccount(trans, static_cast<Account*>(acc))))
1500 xaccSplitDestroy(split);
1501}
1502
1503void
1505{
1506 AccountPrivate *priv;
1507 QofBook *book;
1508
1509 g_return_if_fail(acc);
1510 if (!qof_commit_edit(&acc->inst)) return;
1511
1512 /* If marked for deletion, get rid of subaccounts first,
1513 * and then the splits ... */
1514 priv = GET_PRIVATE(acc);
1516 {
1517 QofCollection *col;
1518
1519 qof_instance_increase_editlevel(acc);
1520
1521 /* First, recursively free children */
1522 xaccFreeAccountChildren(acc);
1523
1524 PINFO ("freeing splits for account %p (%s)",
1525 acc, priv->accountName ? priv->accountName : "(null)");
1526
1527 book = qof_instance_get_book(acc);
1528
1529 /* If book is shutting down, just clear the split list. The splits
1530 themselves will be destroyed by the transaction code */
1531 if (!qof_book_shutting_down(book))
1532 {
1533 // We need to delete in reverse order so that the vector's iterators aren't invalidated.
1534 for_each(priv->splits.rbegin(), priv->splits.rend(), [](Split *s) {
1535 xaccSplitDestroy (s); });
1536 }
1537 else
1538 {
1539 priv->splits.clear();
1540 g_hash_table_remove_all (priv->splits_hash);
1541 }
1542
1543 /* It turns out there's a case where this assertion does not hold:
1544 When the user tries to delete an Imbalance account, while also
1545 deleting all the splits in it. The splits will just get
1546 recreated and put right back into the same account!
1547
1548 g_assert(priv->splits == nullptr || qof_book_shutting_down(acc->inst.book));
1549 */
1550
1551 if (!qof_book_shutting_down(book))
1552 {
1553 col = qof_book_get_collection(book, GNC_ID_TRANS);
1554 qof_collection_foreach(col, destroy_pending_splits_for_account, acc);
1555
1556 /* the lots should be empty by now */
1557 for (auto lp = priv->lots; lp; lp = lp->next)
1558 {
1559 GNCLot *lot = static_cast<GNCLot*>(lp->data);
1560 gnc_lot_destroy (lot);
1561 }
1562 }
1563 g_list_free(priv->lots);
1564 priv->lots = nullptr;
1565
1566 qof_instance_set_dirty(&acc->inst);
1567 qof_instance_decrease_editlevel(acc);
1568 }
1569 else
1570 {
1571 xaccAccountBringUpToDate(acc);
1572 }
1573
1574 qof_commit_edit_part2(&acc->inst, on_err, on_done, acc_free);
1575}
1576
1577void
1579{
1580 g_return_if_fail(GNC_IS_ACCOUNT(acc));
1581
1582 qof_instance_set_destroying(acc, TRUE);
1583
1585}
1586
1587void
1589{
1590 auto priv = GET_PRIVATE(acc);
1591 std::vector<Transaction*> transactions;
1592 transactions.reserve(priv->splits.size());
1593 std::transform(priv->splits.begin(), priv->splits.end(),
1594 back_inserter(transactions),
1595 [](auto split) { return split->parent; });
1596 std::stable_sort(transactions.begin(), transactions.end());
1597 transactions.erase(std::unique(transactions.begin(), transactions.end()),
1598 transactions.end());
1600 std::for_each(transactions.rbegin(), transactions.rend(),
1601 [](auto trans) { xaccTransDestroy (trans); });
1603}
1604
1605/********************************************************************\
1606\********************************************************************/
1607
1608static gboolean
1609xaccAcctChildrenEqual(const AccountVec& na,
1610 const AccountVec& nb,
1611 gboolean check_guids)
1612{
1613 if (na.size() != nb.size())
1614 {
1615 PINFO ("Accounts have different numbers of children");
1616 return (FALSE);
1617 }
1618
1619 for (auto aa : na)
1620 {
1621 auto it_b = std::find_if (nb.begin(), nb.end(), [aa](auto ab) -> bool
1622 {
1623 if (!aa) return (!ab);
1624 if (!ab) return false;
1625 auto code_a{GET_PRIVATE(aa)->accountCode};
1626 auto code_b{GET_PRIVATE(ab)->accountCode};
1627 if ((code_a && *code_a) || (code_b && *code_b)) return !g_strcmp0 (code_a, code_b);
1628 return !g_strcmp0 (GET_PRIVATE(aa)->accountName, GET_PRIVATE(ab)->accountName);
1629 });
1630
1631 if (it_b == nb.end())
1632 {
1633 PINFO ("Unable to find matching child account.");
1634 return FALSE;
1635 }
1636 else if (auto ab = *it_b; !xaccAccountEqual(aa, ab, check_guids))
1637 {
1638 char sa[GUID_ENCODING_LENGTH + 1];
1639 char sb[GUID_ENCODING_LENGTH + 1];
1640
1643
1644 PWARN ("accounts %s and %s differ", sa, sb);
1645
1646 return(FALSE);
1647 }
1648 }
1649
1650 return(TRUE);
1651}
1652
1653gboolean
1654xaccAccountEqual(const Account *aa, const Account *ab, gboolean check_guids)
1655{
1656 AccountPrivate *priv_aa, *priv_ab;
1657
1658 if (!aa && !ab) return TRUE;
1659
1660 g_return_val_if_fail(GNC_IS_ACCOUNT(aa), FALSE);
1661 g_return_val_if_fail(GNC_IS_ACCOUNT(ab), FALSE);
1662
1663 priv_aa = GET_PRIVATE(aa);
1664 priv_ab = GET_PRIVATE(ab);
1665 if (priv_aa->type != priv_ab->type)
1666 {
1667 PWARN ("types differ: %d vs %d", priv_aa->type, priv_ab->type);
1668 return FALSE;
1669 }
1670
1671 if (g_strcmp0(priv_aa->accountName, priv_ab->accountName) != 0)
1672 {
1673 PWARN ("names differ: %s vs %s", priv_aa->accountName, priv_ab->accountName);
1674 return FALSE;
1675 }
1676
1677 if (g_strcmp0(priv_aa->accountCode, priv_ab->accountCode) != 0)
1678 {
1679 PWARN ("codes differ: %s vs %s", priv_aa->accountCode, priv_ab->accountCode);
1680 return FALSE;
1681 }
1682
1683 if (g_strcmp0(priv_aa->description, priv_ab->description) != 0)
1684 {
1685 PWARN ("descriptions differ: %s vs %s", priv_aa->description, priv_ab->description);
1686 return FALSE;
1687 }
1688
1689 if (!gnc_commodity_equal(priv_aa->commodity, priv_ab->commodity))
1690 {
1691 PWARN ("commodities differ");
1692 return FALSE;
1693 }
1694
1695 if (check_guids)
1696 {
1697 if (qof_instance_guid_compare(aa, ab) != 0)
1698 {
1699 PWARN ("GUIDs differ");
1700 return FALSE;
1701 }
1702 }
1703
1704 if (qof_instance_compare_kvp (QOF_INSTANCE (aa), QOF_INSTANCE (ab)) != 0)
1705 {
1706 char *frame_a;
1707 char *frame_b;
1708
1709 frame_a = qof_instance_kvp_as_string (QOF_INSTANCE (aa));
1710 frame_b = qof_instance_kvp_as_string (QOF_INSTANCE (ab));
1711
1712 PWARN ("kvp frames differ:\n%s\n\nvs\n\n%s", frame_a, frame_b);
1713
1714 g_free (frame_a);
1715 g_free (frame_b);
1716
1717 return FALSE;
1718 }
1719
1720 if (!gnc_numeric_equal(priv_aa->starting_balance, priv_ab->starting_balance))
1721 {
1722 char *str_a;
1723 char *str_b;
1724
1725 str_a = gnc_numeric_to_string(priv_aa->starting_balance);
1726 str_b = gnc_numeric_to_string(priv_ab->starting_balance);
1727
1728 PWARN ("starting balances differ: %s vs %s", str_a, str_b);
1729
1730 g_free (str_a);
1731 g_free (str_b);
1732
1733 return FALSE;
1734 }
1735
1736 if (!gnc_numeric_equal(priv_aa->starting_noclosing_balance,
1737 priv_ab->starting_noclosing_balance))
1738 {
1739 char *str_a;
1740 char *str_b;
1741
1742 str_a = gnc_numeric_to_string(priv_aa->starting_noclosing_balance);
1743 str_b = gnc_numeric_to_string(priv_ab->starting_noclosing_balance);
1744
1745 PWARN ("starting noclosing balances differ: %s vs %s", str_a, str_b);
1746
1747 g_free (str_a);
1748 g_free (str_b);
1749
1750 return FALSE;
1751 }
1752 if (!gnc_numeric_equal(priv_aa->starting_cleared_balance,
1753 priv_ab->starting_cleared_balance))
1754 {
1755 char *str_a;
1756 char *str_b;
1757
1758 str_a = gnc_numeric_to_string(priv_aa->starting_cleared_balance);
1759 str_b = gnc_numeric_to_string(priv_ab->starting_cleared_balance);
1760
1761 PWARN ("starting cleared balances differ: %s vs %s", str_a, str_b);
1762
1763 g_free (str_a);
1764 g_free (str_b);
1765
1766 return FALSE;
1767 }
1768
1769 if (!gnc_numeric_equal(priv_aa->starting_reconciled_balance,
1770 priv_ab->starting_reconciled_balance))
1771 {
1772 char *str_a;
1773 char *str_b;
1774
1775 str_a = gnc_numeric_to_string(priv_aa->starting_reconciled_balance);
1776 str_b = gnc_numeric_to_string(priv_ab->starting_reconciled_balance);
1777
1778 PWARN ("starting reconciled balances differ: %s vs %s", str_a, str_b);
1779
1780 g_free (str_a);
1781 g_free (str_b);
1782
1783 return FALSE;
1784 }
1785
1786 if (!gnc_numeric_equal(priv_aa->balance, priv_ab->balance))
1787 {
1788 char *str_a;
1789 char *str_b;
1790
1791 str_a = gnc_numeric_to_string(priv_aa->balance);
1792 str_b = gnc_numeric_to_string(priv_ab->balance);
1793
1794 PWARN ("balances differ: %s vs %s", str_a, str_b);
1795
1796 g_free (str_a);
1797 g_free (str_b);
1798
1799 return FALSE;
1800 }
1801
1802 if (!gnc_numeric_equal(priv_aa->noclosing_balance, priv_ab->noclosing_balance))
1803 {
1804 char *str_a;
1805 char *str_b;
1806
1807 str_a = gnc_numeric_to_string(priv_aa->noclosing_balance);
1808 str_b = gnc_numeric_to_string(priv_ab->noclosing_balance);
1809
1810 PWARN ("noclosing balances differ: %s vs %s", str_a, str_b);
1811
1812 g_free (str_a);
1813 g_free (str_b);
1814
1815 return FALSE;
1816 }
1817 if (!gnc_numeric_equal(priv_aa->cleared_balance, priv_ab->cleared_balance))
1818 {
1819 char *str_a;
1820 char *str_b;
1821
1822 str_a = gnc_numeric_to_string(priv_aa->cleared_balance);
1823 str_b = gnc_numeric_to_string(priv_ab->cleared_balance);
1824
1825 PWARN ("cleared balances differ: %s vs %s", str_a, str_b);
1826
1827 g_free (str_a);
1828 g_free (str_b);
1829
1830 return FALSE;
1831 }
1832
1833 if (!gnc_numeric_equal(priv_aa->reconciled_balance, priv_ab->reconciled_balance))
1834 {
1835 char *str_a;
1836 char *str_b;
1837
1838 str_a = gnc_numeric_to_string(priv_aa->reconciled_balance);
1839 str_b = gnc_numeric_to_string(priv_ab->reconciled_balance);
1840
1841 PWARN ("reconciled balances differ: %s vs %s", str_a, str_b);
1842
1843 g_free (str_a);
1844 g_free (str_b);
1845
1846 return FALSE;
1847 }
1848
1849 /* no parent; always compare downwards. */
1850
1851 if (!std::equal (priv_aa->splits.begin(), priv_aa->splits.end(),
1852 priv_ab->splits.begin(), priv_ab->splits.end(),
1853 [check_guids](auto sa, auto sb)
1854 { return xaccSplitEqual(sa, sb, check_guids, true, false); }))
1855 {
1856 PWARN ("splits differ");
1857 return false;
1858 }
1859
1860 if (!xaccAcctChildrenEqual(priv_aa->children, priv_ab->children, check_guids))
1861 {
1862 PWARN ("children differ");
1863 return FALSE;
1864 }
1865
1866 return(TRUE);
1867}
1868
1869/********************************************************************\
1870\********************************************************************/
1871void
1873{
1874 AccountPrivate *priv;
1875
1876 g_return_if_fail(GNC_IS_ACCOUNT(acc));
1877
1879 return;
1880
1881 priv = GET_PRIVATE(acc);
1882 priv->sort_dirty = TRUE;
1883}
1884
1885void
1887{
1888 AccountPrivate *priv;
1889
1890 g_return_if_fail(GNC_IS_ACCOUNT(acc));
1891
1893 return;
1894
1895 priv = GET_PRIVATE(acc);
1896 priv->balance_dirty = TRUE;
1897}
1898
1900{
1901 AccountPrivate *priv;
1902
1903 g_return_if_fail (GNC_IS_ACCOUNT (acc));
1904
1906 return;
1907
1908 priv = GET_PRIVATE (acc);
1909 priv->defer_bal_computation = defer;
1910}
1911
1913{
1914 AccountPrivate *priv;
1915 if (!acc)
1916 return false;
1917 priv = GET_PRIVATE (acc);
1918 return priv->defer_bal_computation;
1919}
1920
1921
1922/********************************************************************\
1923\********************************************************************/
1924
1925static bool split_cmp_less (const Split* a, const Split* b)
1926{
1927 return xaccSplitOrder (a, b) < 0;
1928}
1929
1930gboolean
1932{
1933 AccountPrivate *priv;
1934
1935 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
1936 g_return_val_if_fail(GNC_IS_SPLIT(s), FALSE);
1937
1938 priv = GET_PRIVATE(acc);
1939 if (!g_hash_table_add (priv->splits_hash, s))
1940 return false;
1941
1942 priv->splits.push_back (s);
1943
1944 if (qof_instance_get_editlevel(acc) == 0)
1945 std::sort (priv->splits.begin(), priv->splits.end(), split_cmp_less);
1946 else
1947 priv->sort_dirty = true;
1948
1949 //FIXME: find better event
1950 qof_event_gen (&acc->inst, QOF_EVENT_MODIFY, nullptr);
1951 /* Also send an event based on the account */
1952 qof_event_gen(&acc->inst, GNC_EVENT_ITEM_ADDED, s);
1953
1954 priv->balance_dirty = TRUE;
1955// DRH: Should the below be added? It is present in the delete path.
1956// xaccAccountRecomputeBalance(acc);
1957 return TRUE;
1958}
1959
1960gboolean
1962{
1963 AccountPrivate *priv;
1964
1965 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
1966 g_return_val_if_fail(GNC_IS_SPLIT(s), FALSE);
1967
1968 priv = GET_PRIVATE(acc);
1969
1970 if (!g_hash_table_remove (priv->splits_hash, s))
1971 return false;
1972
1973 // shortcut pruning the last element. this is the most common
1974 // remove_split operation during UI or book shutdown.
1975 if (s == priv->splits.back())
1976 priv->splits.pop_back();
1977 else
1978 priv->splits.erase (std::remove (priv->splits.begin(), priv->splits.end(), s),
1979 priv->splits.end());
1980
1981 //FIXME: find better event type
1982 qof_event_gen(&acc->inst, QOF_EVENT_MODIFY, nullptr);
1983 // And send the account-based event, too
1984 qof_event_gen(&acc->inst, GNC_EVENT_ITEM_REMOVED, s);
1985
1986 priv->balance_dirty = TRUE;
1988 return TRUE;
1989}
1990
1991void
1992xaccAccountSortSplits (Account *acc, gboolean force)
1993{
1994 AccountPrivate *priv;
1995
1996 g_return_if_fail(GNC_IS_ACCOUNT(acc));
1997
1998 priv = GET_PRIVATE(acc);
1999 if (!priv->sort_dirty || (!force && qof_instance_get_editlevel(acc) > 0))
2000 return;
2001 std::sort (priv->splits.begin(), priv->splits.end(), split_cmp_less);
2002 priv->sort_dirty = FALSE;
2003 priv->balance_dirty = TRUE;
2004}
2005
2006static void
2007xaccAccountBringUpToDate(Account *acc)
2008{
2009 if (!acc) return;
2010
2011 /* if a re-sort happens here, then everything will update, so the
2012 cost basis and balance calls are no-ops */
2013 xaccAccountSortSplits(acc, FALSE);
2015}
2016
2017/********************************************************************\
2018\********************************************************************/
2019
2020void
2021xaccAccountSetGUID (Account *acc, const GncGUID *guid)
2022{
2023 g_return_if_fail(GNC_IS_ACCOUNT(acc));
2024 g_return_if_fail(guid);
2025
2026 /* XXX this looks fishy and weird to me ... */
2027 PINFO("acct=%p", acc);
2029 qof_instance_set_guid (&acc->inst, guid);
2030 qof_instance_set_dirty(&acc->inst);
2032}
2033
2034/********************************************************************\
2035\********************************************************************/
2036
2037Account *
2039{
2040 QofCollection *col;
2041 if (!guid || !book) return nullptr;
2042 col = qof_book_get_collection (book, GNC_ID_ACCOUNT);
2043 return (Account *) qof_collection_lookup_entity (col, guid);
2044}
2045
2046/********************************************************************\
2047\********************************************************************/
2048
2049void
2051{
2052 AccountPrivate *priv;
2053
2054 g_return_if_fail(GNC_IS_ACCOUNT(acc));
2055
2056 priv = GET_PRIVATE(acc);
2057 priv->mark = m;
2058}
2059
2060void
2061xaccClearMark (Account *acc, short val)
2062{
2063 Account *root;
2064
2065 g_return_if_fail(GNC_IS_ACCOUNT(acc));
2066
2067 root = gnc_account_get_root(acc);
2068 xaccClearMarkDown(root ? root : acc, val);
2069}
2070
2071void
2073{
2074 AccountPrivate *priv;
2075
2076 g_return_if_fail(GNC_IS_ACCOUNT(acc));
2077
2078 priv = GET_PRIVATE(acc);
2079 priv->mark = val;
2080 std::for_each (priv->children.begin(), priv->children.end(),
2081 [val](auto acc){ xaccClearMarkDown(acc, val); });
2082}
2083
2084/********************************************************************\
2085\********************************************************************/
2086
2087GNCPolicy *
2089{
2090 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
2091
2092 return GET_PRIVATE(acc)->policy;
2093}
2094
2095void
2096gnc_account_set_policy (Account *acc, GNCPolicy *policy)
2097{
2098 AccountPrivate *priv;
2099
2100 g_return_if_fail(GNC_IS_ACCOUNT(acc));
2101
2102 priv = GET_PRIVATE(acc);
2103 priv->policy = policy ? policy : xaccGetFIFOPolicy();
2104}
2105
2106/********************************************************************\
2107\********************************************************************/
2108
2109void
2110xaccAccountRemoveLot (Account *acc, GNCLot *lot)
2111{
2112 AccountPrivate *priv;
2113
2114 g_return_if_fail(GNC_IS_ACCOUNT(acc));
2115 g_return_if_fail(GNC_IS_LOT(lot));
2116
2117 priv = GET_PRIVATE(acc);
2118 g_return_if_fail(priv->lots);
2119
2120 ENTER ("(acc=%p, lot=%p)", acc, lot);
2121 priv->lots = g_list_remove(priv->lots, lot);
2122 qof_event_gen (QOF_INSTANCE(lot), QOF_EVENT_REMOVE, nullptr);
2123 qof_event_gen (&acc->inst, QOF_EVENT_MODIFY, nullptr);
2124 LEAVE ("(acc=%p, lot=%p)", acc, lot);
2125}
2126
2127void
2129{
2130 /* errors */
2131 g_return_if_fail(GNC_IS_ACCOUNT(acc));
2132 g_return_if_fail(GNC_IS_LOT(lot));
2133
2134 /* optimizations */
2135 auto lot_acc = gnc_lot_get_account(lot);
2136 if (lot_acc == acc)
2137 return;
2138
2139 ENTER ("(acc=%p, lot=%p)", acc, lot);
2140
2141 /* pull it out of the old account */
2142 if (lot_acc)
2143 {
2144 auto priv = GET_PRIVATE(lot_acc);
2145 priv->lots = g_list_remove(priv->lots, lot);
2146 qof_event_gen (&lot_acc->inst, QOF_EVENT_MODIFY, nullptr);
2147 }
2148
2149 auto priv = GET_PRIVATE(acc);
2150 priv->lots = g_list_prepend(priv->lots, lot);
2151 gnc_lot_set_account(lot, acc);
2152
2153 /* Don't move the splits to the new account. The caller will do this
2154 * if appropriate, and doing it here will not work if we are being
2155 * called from gnc_book_close_period since xaccAccountInsertSplit
2156 * will try to balance capital gains and things aren't ready for that. */
2157
2158 qof_event_gen (QOF_INSTANCE(lot), QOF_EVENT_ADD, nullptr);
2159 qof_event_gen (&acc->inst, QOF_EVENT_MODIFY, nullptr);
2160
2161 LEAVE ("(acc=%p, lot=%p)", acc, lot);
2162}
2163
2164/********************************************************************\
2165\********************************************************************/
2166static void
2167xaccPreSplitMove (Split *split)
2168{
2170}
2171
2172static void
2173xaccPostSplitMove (Split *split, Account *accto)
2174{
2175 Transaction *trans;
2176
2177 xaccSplitSetAccount(split, accto);
2178 xaccSplitSetAmount(split, split->amount);
2179 trans = xaccSplitGetParent (split);
2180 xaccTransCommitEdit (trans);
2181}
2182
2183void
2185{
2186 AccountPrivate *from_priv;
2187
2188 /* errors */
2189 g_return_if_fail(GNC_IS_ACCOUNT(accfrom));
2190 g_return_if_fail(GNC_IS_ACCOUNT(accto));
2191
2192 /* optimizations */
2193 from_priv = GET_PRIVATE(accfrom);
2194 if (from_priv->splits.empty() || accfrom == accto)
2195 return;
2196
2197 /* check for book mix-up */
2198 g_return_if_fail (qof_instance_books_equal(accfrom, accto));
2199 ENTER ("(accfrom=%p, accto=%p)", accfrom, accto);
2200
2201 xaccAccountBeginEdit(accfrom);
2202 xaccAccountBeginEdit(accto);
2203 /* Begin editing both accounts and all transactions in accfrom. */
2204 std::for_each (from_priv->splits.begin(), from_priv->splits.end(), xaccPreSplitMove);
2205
2206 /* Concatenate accfrom's lists of splits and lots to accto's lists. */
2207 //to_priv->splits = g_list_concat(to_priv->splits, from_priv->splits);
2208 //to_priv->lots = g_list_concat(to_priv->lots, from_priv->lots);
2209
2210 /* Set appropriate flags. */
2211 //from_priv->balance_dirty = TRUE;
2212 //from_priv->sort_dirty = FALSE;
2213 //to_priv->balance_dirty = TRUE;
2214 //to_priv->sort_dirty = TRUE;
2215
2216 /*
2217 * Change each split's account back pointer to accto.
2218 * Convert each split's amount to accto's commodity.
2219 * Commit to editing each transaction.
2220 */
2221 auto splits = from_priv->splits;
2222 std::for_each (splits.begin(), splits.end(), [accto](auto s){ xaccPostSplitMove (s, accto); });
2223
2224 /* Finally empty accfrom. */
2225 g_assert(from_priv->splits.empty());
2226 g_assert(from_priv->lots == nullptr);
2227 xaccAccountCommitEdit(accfrom);
2228 xaccAccountCommitEdit(accto);
2229
2230 LEAVE ("(accfrom=%p, accto=%p)", accfrom, accto);
2231}
2232
2233
2234/********************************************************************\
2235 * xaccAccountRecomputeBalance *
2236 * recomputes the partial balances and the current balance for *
2237 * this account. *
2238 * *
2239 * The way the computation is done depends on whether the partial *
2240 * balances are for a monetary account (bank, cash, etc.) or a *
2241 * certificate account (stock portfolio, mutual fund). For bank *
2242 * accounts, the invariant amount is the dollar amount. For share *
2243 * accounts, the invariant amount is the number of shares. For *
2244 * share accounts, the share price fluctuates, and the current *
2245 * value of such an account is the number of shares times the *
2246 * current share price. *
2247 * *
2248 * Part of the complexity of this computation stems from the fact *
2249 * xacc uses a double-entry system, meaning that one transaction *
2250 * appears in two accounts: one account is debited, and the other *
2251 * is credited. When the transaction represents a sale of shares, *
2252 * or a purchase of shares, some care must be taken to compute *
2253 * balances correctly. For a sale of shares, the stock account must*
2254 * be debited in shares, but the bank account must be credited *
2255 * in dollars. Thus, two different mechanisms must be used to *
2256 * compute balances, depending on account type. *
2257 * *
2258 * Args: account -- the account for which to recompute balances *
2259 * Return: void *
2260\********************************************************************/
2261
2262void
2264{
2265 if (nullptr == acc) return;
2266
2267 auto priv = GET_PRIVATE(acc);
2268 if (qof_instance_get_editlevel(acc) > 0) return;
2269 if (!priv->balance_dirty || priv->defer_bal_computation) return;
2270 if (qof_instance_get_destroying(acc)) return;
2272
2273 auto balance = priv->starting_balance;
2274 auto noclosing_balance = priv->starting_noclosing_balance;
2275 auto cleared_balance = priv->starting_cleared_balance;
2276 auto reconciled_balance = priv->starting_reconciled_balance;
2277 auto has_stock_split = false;
2278
2279 PINFO ("acct=%s starting baln=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT,
2280 priv->accountName, balance.num, balance.denom);
2281 for (auto split : priv->splits)
2282 {
2283 auto amt = xaccSplitGetAmount (split);
2284
2285 if (xaccSplitIsStockSplit(split))
2286 {
2287 if (gnc_numeric_zero_p(balance))
2288 continue;
2289 auto new_balance = gnc_numeric_add_fixed(balance, amt);
2290 if (gnc_numeric_zero_p(new_balance))
2291 continue;
2292 auto ratio = gnc_numeric_div(new_balance, balance, GNC_DENOM_AUTO,
2294 auto denom = xaccAccountGetCommoditySCU(acc);
2295 for (auto psplit : priv->splits)
2296 {
2297 if (psplit == split)
2298 break;
2300 gnc_numeric_mul(xaccSplitGetAdjustedAmount(psplit), ratio, denom,
2302 }
2303 balance = new_balance;
2304 has_stock_split = true;
2305 }
2306 else
2307 {
2308 split->adjusted_amount = amt;
2309 balance = gnc_numeric_add_fixed(balance, amt);
2310 }
2311
2312 if (NREC != split->reconciled)
2313 {
2314 cleared_balance = gnc_numeric_add_fixed(cleared_balance, amt);
2315 }
2316
2317 if (YREC == split->reconciled ||
2318 FREC == split->reconciled)
2319 {
2320 reconciled_balance =
2321 gnc_numeric_add_fixed(reconciled_balance, amt);
2322 }
2323
2324 if (!(xaccTransGetIsClosingTxn (split->parent)))
2325 noclosing_balance = gnc_numeric_add_fixed(noclosing_balance, amt);
2326
2327 split->balance = balance;
2328 split->noclosing_balance = noclosing_balance;
2329 split->cleared_balance = cleared_balance;
2330 split->reconciled_balance = reconciled_balance;
2331
2332 }
2333
2334 priv->balance = balance;
2335 priv->noclosing_balance = noclosing_balance;
2336 priv->cleared_balance = cleared_balance;
2337 priv->reconciled_balance = reconciled_balance;
2338 priv->balance_dirty = FALSE;
2339 priv->has_stock_split = has_stock_split;
2340}
2341
2342/********************************************************************\
2343\********************************************************************/
2344
2345/* The sort order is used to implicitly define an
2346 * order for report generation */
2347
2348static int typeorder[NUM_ACCOUNT_TYPES] =
2349{
2354};
2355
2356static int revorder[NUM_ACCOUNT_TYPES] =
2357{
2358 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
2359};
2360
2361
2362int
2363xaccAccountOrder (const Account *aa, const Account *ab)
2364{
2365 AccountPrivate *priv_aa, *priv_ab;
2366 const char *da, *db;
2367 int ta, tb, result;
2368
2369 if (aa == ab) return 0;
2370 if (!ab) return -1;
2371 if (!aa) return +1;
2372
2373 priv_aa = GET_PRIVATE(aa);
2374 priv_ab = GET_PRIVATE(ab);
2375
2376 /* sort on accountCode strings */
2377 da = priv_aa->accountCode;
2378 db = priv_ab->accountCode;
2379
2380 /* Otherwise do a string sort */
2381 result = g_strcmp0 (da, db);
2382 if (result)
2383 return result;
2384
2385 /* if account-type-order array not initialized, initialize it */
2386 /* this will happen at most once during program invocation */
2387 if (-1 == revorder[0])
2388 {
2389 int i;
2390 for (i = 0; i < NUM_ACCOUNT_TYPES; i++)
2391 {
2392 revorder [typeorder[i]] = i;
2393 }
2394 }
2395
2396 /* otherwise, sort on account type */
2397 ta = priv_aa->type;
2398 tb = priv_ab->type;
2399 ta = revorder[ta];
2400 tb = revorder[tb];
2401 if (ta < tb) return -1;
2402 if (ta > tb) return +1;
2403
2404 /* otherwise, sort on accountName strings */
2405 da = priv_aa->accountName;
2406 db = priv_ab->accountName;
2407 result = safe_utf8_collate(da, db);
2408 if (result)
2409 return result;
2410
2411 /* guarantee a stable sort */
2412 return qof_instance_guid_compare(aa, ab);
2413}
2414
2415static int
2416qof_xaccAccountOrder (const Account **aa, const Account **ab)
2417{
2418 return xaccAccountOrder(*aa, *ab);
2419}
2420
2421/********************************************************************\
2422\********************************************************************/
2423
2424void
2426{
2427 AccountPrivate *priv;
2428
2429 /* errors */
2430 g_return_if_fail(GNC_IS_ACCOUNT(acc));
2431 g_return_if_fail(tip < NUM_ACCOUNT_TYPES);
2432
2433 /* optimizations */
2434 priv = GET_PRIVATE(acc);
2435 if (priv->type == tip)
2436 return;
2437
2439 priv->type = tip;
2440 priv->balance_dirty = TRUE; /* new type may affect balance computation */
2441 mark_account(acc);
2443}
2444
2445void
2446xaccAccountSetName (Account *acc, const char *str)
2447{
2448 AccountPrivate *priv;
2449
2450 /* errors */
2451 g_return_if_fail(GNC_IS_ACCOUNT(acc));
2452 g_return_if_fail(str);
2453
2454 /* optimizations */
2455 priv = GET_PRIVATE(acc);
2456 if (g_strcmp0(str, priv->accountName) == 0)
2457 return;
2458
2460 priv->accountName = qof_string_cache_replace(priv->accountName, str);
2461 mark_account (acc);
2463}
2464
2465void
2466xaccAccountSetCode (Account *acc, const char *str)
2467{
2468 AccountPrivate *priv;
2469
2470 /* errors */
2471 g_return_if_fail(GNC_IS_ACCOUNT(acc));
2472
2473 /* optimizations */
2474 priv = GET_PRIVATE(acc);
2475 if (g_strcmp0(str, priv->accountCode) == 0)
2476 return;
2477
2479 priv->accountCode = qof_string_cache_replace(priv->accountCode, str ? str : "");
2480 mark_account (acc);
2482}
2483
2484void
2485xaccAccountSetDescription (Account *acc, const char *str)
2486{
2487 AccountPrivate *priv;
2488
2489 /* errors */
2490 g_return_if_fail(GNC_IS_ACCOUNT(acc));
2491
2492 /* optimizations */
2493 priv = GET_PRIVATE(acc);
2494 if (g_strcmp0(str, priv->description) == 0)
2495 return;
2496
2498 priv->description = qof_string_cache_replace(priv->description, str ? str : "");
2499 mark_account (acc);
2501}
2502
2503static void
2504set_kvp_gnc_numeric_path (Account *acc, const std::vector<std::string>& path,
2505 std::optional<gnc_numeric> value)
2506{
2508 qof_instance_set_path_kvp<gnc_numeric> (QOF_INSTANCE(acc), value, path);
2510}
2511
2512static std::optional<gnc_numeric>
2513get_kvp_gnc_numeric_path (const Account *acc, const Path& path)
2514{
2515 return qof_instance_get_path_kvp<gnc_numeric> (QOF_INSTANCE(acc), path);
2516}
2517
2518static void
2519set_kvp_string_path (Account *acc, std::vector<std::string> const & path,
2520 const char *value)
2521{
2522 std::optional<const char*> val;
2523 if (value && *value)
2524 val = g_strdup(value);
2525
2527 qof_instance_set_path_kvp<const char*> (QOF_INSTANCE(acc), val, path);
2529}
2530
2531static const char*
2532get_kvp_string_path (const Account *acc, const Path& path)
2533{
2534 auto rv{qof_instance_get_path_kvp<const char*> (QOF_INSTANCE(acc), path)};
2535 return rv ? *rv : nullptr;
2536}
2537
2538static void
2539set_kvp_account_path (Account* acc, const Path& path, const Account* kvp_account)
2540{
2541 std::optional<GncGUID*> val;
2542 if (kvp_account)
2543 val = guid_copy(xaccAccountGetGUID (kvp_account));
2544
2546 qof_instance_set_path_kvp<GncGUID*> (QOF_INSTANCE(acc), val, path);
2548}
2549
2550static Account*
2551get_kvp_account_path (const Account *acc, const Path& path)
2552{
2553 auto val{qof_instance_get_path_kvp<GncGUID*> (QOF_INSTANCE(acc), path)};
2554 return val ? xaccAccountLookup (*val, gnc_account_get_book (acc)) : nullptr;
2555}
2556
2557static void
2558set_kvp_boolean_path (Account *acc, const Path& path, gboolean option)
2559{
2560 set_kvp_string_path (acc, path, option ? "true" : nullptr);
2561}
2562
2563static gboolean
2564get_kvp_boolean_path (const Account *acc, const Path& path)
2565{
2566 auto slot{QOF_INSTANCE(acc)->kvp_data->get_slot(path)};
2567 if (!slot) return false;
2568 switch (slot->get_type())
2569 {
2571 return slot->get<int64_t>() != 0;
2573 return g_strcmp0 (slot->get<const char*>(), "true") == 0;
2574 default:
2575 return false;
2576 }
2577}
2578
2579static void
2580set_kvp_int64_path (Account *acc, const Path& path, std::optional<gint64> value)
2581{
2583 qof_instance_set_path_kvp<int64_t> (QOF_INSTANCE(acc), value, path);
2585}
2586
2587static const std::optional<gint64>
2588get_kvp_int64_path (const Account *acc, const Path& path)
2589{
2590 return qof_instance_get_path_kvp<int64_t> (QOF_INSTANCE(acc), path);
2591}
2592
2593static GncGUID*
2594get_kvp_guid_path (const Account *acc, const Path& path)
2595{
2596 auto val{qof_instance_get_path_kvp<GncGUID*> (QOF_INSTANCE(acc), path)};
2597 return val ? guid_copy(*val) : nullptr;
2598}
2599
2600void
2601xaccAccountSetColor (Account *acc, const char *str)
2602{
2603 set_kvp_string_path (acc, {"color"}, str);
2604}
2605
2606void
2607xaccAccountSetFilter (Account *acc, const char *str)
2608{
2609 set_kvp_string_path (acc, {"filter"}, str);
2610}
2611
2612void
2613xaccAccountSetSortOrder (Account *acc, const char *str)
2614{
2615 set_kvp_string_path (acc, {"sort-order"}, str);
2616}
2617
2618void
2619xaccAccountSetSortReversed (Account *acc, gboolean sortreversed)
2620{
2621 set_kvp_boolean_path (acc, {"sort-reversed"}, sortreversed);
2622}
2623
2624static void
2625qofAccountSetParent (Account *acc, QofInstance *parent)
2626{
2627 Account *parent_acc;
2628
2629 g_return_if_fail(GNC_IS_ACCOUNT(acc));
2630 g_return_if_fail(GNC_IS_ACCOUNT(parent));
2631
2632 parent_acc = GNC_ACCOUNT(parent);
2634 xaccAccountBeginEdit(parent_acc);
2635 gnc_account_append_child(parent_acc, acc);
2636 mark_account (parent_acc);
2637 mark_account (acc);
2639 xaccAccountCommitEdit(parent_acc);
2640}
2641
2642void
2643xaccAccountSetNotes (Account *acc, const char *str)
2644{
2645 set_kvp_string_path (acc, {"notes"}, str);
2646}
2647
2648void
2649xaccAccountSetOnlineID (Account *acc, const char *id)
2650{
2651 g_return_if_fail (GNC_IS_ACCOUNT(acc));
2652 set_kvp_string_path (acc, {KEY_ONLINE_ID}, id);
2653}
2654
2655
2656void
2657xaccAccountSetAssociatedAccount (Account *acc, const char *tag, const Account* assoc_acct)
2658{
2659 g_return_if_fail (GNC_IS_ACCOUNT(acc));
2660 g_return_if_fail (tag && *tag);
2661
2662 set_kvp_account_path (acc, {"associated-account", tag}, assoc_acct);
2663}
2664
2665void
2666xaccAccountSetCommodity (Account * acc, gnc_commodity * com)
2667{
2668 AccountPrivate *priv;
2669
2670 /* errors */
2671 g_return_if_fail(GNC_IS_ACCOUNT(acc));
2672 g_return_if_fail(GNC_IS_COMMODITY(com));
2673
2674 /* optimizations */
2675 priv = GET_PRIVATE(acc);
2676 if (com == priv->commodity)
2677 return;
2678
2680 gnc_commodity_decrement_usage_count(priv->commodity);
2681 priv->commodity = com;
2683 priv->commodity_scu = gnc_commodity_get_fraction(com);
2684 priv->non_standard_scu = FALSE;
2685
2686 /* iterate over splits */
2687 for (auto s : priv->splits)
2688 {
2689 Transaction *trans = xaccSplitGetParent (s);
2690
2691 xaccTransBeginEdit (trans);
2693 xaccTransCommitEdit (trans);
2694 }
2695
2696 priv->sort_dirty = TRUE; /* Not needed. */
2697 priv->balance_dirty = TRUE;
2698 mark_account (acc);
2699
2701}
2702
2703/*
2704 * Set the account scu and then check to see if it is the same as the
2705 * commodity scu. This function is called when parsing the data file
2706 * and is designed to catch cases where the two were accidentally set
2707 * to mismatched values in the past.
2708 */
2709void
2711{
2712 AccountPrivate *priv;
2713
2714 g_return_if_fail(GNC_IS_ACCOUNT(acc));
2715
2716 priv = GET_PRIVATE(acc);
2718 priv->commodity_scu = scu;
2719 if (scu != gnc_commodity_get_fraction(priv->commodity))
2720 priv->non_standard_scu = TRUE;
2721 mark_account(acc);
2723}
2724
2725int
2727{
2728 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), 0);
2729 return GET_PRIVATE(acc)->commodity_scu;
2730}
2731
2732int
2734{
2735 AccountPrivate *priv;
2736
2737 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), 0);
2738
2739 priv = GET_PRIVATE(acc);
2740 if (priv->non_standard_scu || !priv->commodity)
2741 return priv->commodity_scu;
2742 return gnc_commodity_get_fraction(priv->commodity);
2743}
2744
2745void
2747{
2748 AccountPrivate *priv;
2749
2750 g_return_if_fail(GNC_IS_ACCOUNT(acc));
2751
2752 priv = GET_PRIVATE(acc);
2753 if (priv->non_standard_scu == flag)
2754 return;
2756 priv->non_standard_scu = flag;
2757 mark_account (acc);
2759}
2760
2761gboolean
2763{
2764 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), 0);
2765 return GET_PRIVATE(acc)->non_standard_scu;
2766}
2767
2768/********************************************************************\
2769\********************************************************************/
2770/* below follow the old, deprecated currency/security routines. */
2771
2772void
2773DxaccAccountSetCurrency (Account * acc, gnc_commodity * currency)
2774{
2775 if ((!acc) || (!currency)) return;
2776
2777 auto s = gnc_commodity_get_unique_name (currency);
2778 set_kvp_string_path (acc, {"old-currency"}, s);
2779
2780 auto book = qof_instance_get_book(acc);
2782 auto commodity = gnc_commodity_table_lookup_unique (table, s);
2783
2784 if (!commodity)
2786}
2787
2788/********************************************************************\
2789\********************************************************************/
2790
2791void
2792gnc_account_foreach_descendant (const Account *acc, std::function<void(Account*)> account_cb)
2793{
2794 g_return_if_fail (GNC_IS_ACCOUNT(acc));
2795
2796 // children must be a vector copy instead of reference because
2797 // some callers e.g. xaccAccountTreeScrubLots will modify the
2798 // children
2799 auto children = GET_PRIVATE(acc)->children;
2800 for (auto child : children)
2801 {
2802 account_cb (child);
2803 gnc_account_foreach_descendant (child, account_cb);
2804 }
2805}
2806
2807static void
2808account_foreach_descendant_sorted (const Account *acc, std::function<void(Account*)> account_cb)
2809{
2810 g_return_if_fail (GNC_IS_ACCOUNT(acc));
2811
2812 auto children = GET_PRIVATE(acc)->children;
2813 std::sort (children.begin(), children.end(),
2814 [](auto a, auto b) { return xaccAccountOrder (a, b) < 0; });
2815
2816 for (auto child : children)
2817 {
2818 account_cb (child);
2819 account_foreach_descendant_sorted (child, account_cb);
2820 }
2821}
2822
2823void
2825{
2826 AccountPrivate *ppriv, *cpriv;
2827 Account *old_parent;
2828 QofCollection *col;
2829
2830 /* errors */
2831 g_assert(GNC_IS_ACCOUNT(new_parent));
2832 g_assert(GNC_IS_ACCOUNT(child));
2833
2834 /* optimizations */
2835 ppriv = GET_PRIVATE(new_parent);
2836 cpriv = GET_PRIVATE(child);
2837 old_parent = cpriv->parent;
2838 if (old_parent == new_parent)
2839 return;
2840
2841 // xaccAccountBeginEdit(new_parent);
2842 xaccAccountBeginEdit(child);
2843 if (old_parent)
2844 {
2845 gnc_account_remove_child(old_parent, child);
2846
2847 if (!qof_instance_books_equal(old_parent, new_parent))
2848 {
2849 /* hack alert -- this implementation is not exactly correct.
2850 * If the entity tables are not identical, then the 'from' book
2851 * may have a different backend than the 'to' book. This means
2852 * that we should get the 'from' backend to destroy this account,
2853 * and the 'to' backend to save it. Right now, this is broken.
2854 *
2855 * A 'correct' implementation similar to this is in Period.c
2856 * except its for transactions ...
2857 *
2858 * Note also, we need to reparent the children to the new book as well.
2859 */
2860 PWARN ("reparenting accounts across books is not correctly supported\n");
2861
2862 qof_event_gen (&child->inst, QOF_EVENT_DESTROY, nullptr);
2864 GNC_ID_ACCOUNT);
2865 qof_collection_insert_entity (col, &child->inst);
2866 qof_event_gen (&child->inst, QOF_EVENT_CREATE, nullptr);
2867 }
2868 }
2869 cpriv->parent = new_parent;
2870 ppriv->children.push_back (child);
2871 qof_instance_set_dirty(&new_parent->inst);
2872 qof_instance_set_dirty(&child->inst);
2873
2874 /* Send events data. Warning: The call to commit_edit is also going
2875 * to send a MODIFY event. If the gtktreemodelfilter code gets the
2876 * MODIFY before it gets the ADD, it gets very confused and thinks
2877 * that two nodes have been added. */
2878 qof_event_gen (&child->inst, QOF_EVENT_ADD, nullptr);
2879 // qof_event_gen (&new_parent->inst, QOF_EVENT_MODIFY, nullptr);
2880
2881 xaccAccountCommitEdit (child);
2882 // xaccAccountCommitEdit(new_parent);
2883}
2884
2885void
2887{
2888 AccountPrivate *ppriv, *cpriv;
2889 GncEventData ed;
2890
2891 if (!child) return;
2892
2893 /* Note this routine might be called on accounts which
2894 * are not yet parented. */
2895 if (!parent) return;
2896
2897 ppriv = GET_PRIVATE(parent);
2898 cpriv = GET_PRIVATE(child);
2899
2900 if (cpriv->parent != parent)
2901 {
2902 PERR ("account not a child of parent");
2903 return;
2904 }
2905
2906 /* Gather event data */
2907 ed.node = parent;
2908 ed.idx = gnc_account_child_index (parent, child);
2909
2910 ppriv->children.erase (std::remove (ppriv->children.begin(), ppriv->children.end(), child),
2911 ppriv->children.end());
2912
2913 /* Now send the event. */
2914 qof_event_gen(&child->inst, QOF_EVENT_REMOVE, &ed);
2915
2916 /* clear the account's parent pointer after REMOVE event generation. */
2917 cpriv->parent = nullptr;
2918
2919 qof_event_gen (&parent->inst, QOF_EVENT_MODIFY, nullptr);
2920}
2921
2922Account *
2924{
2925 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
2926 return GET_PRIVATE(acc)->parent;
2927}
2928
2929Account *
2931{
2932 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
2933
2934 while (auto parent = gnc_account_get_parent (acc))
2935 acc = parent;
2936
2937 return acc;
2938}
2939
2940gboolean
2942{
2943 g_return_val_if_fail(GNC_IS_ACCOUNT(account), FALSE);
2944 return (GET_PRIVATE(account)->parent == nullptr);
2945}
2946
2947GList *
2949{
2950 g_return_val_if_fail(GNC_IS_ACCOUNT(account), nullptr);
2951 auto& children = GET_PRIVATE(account)->children;
2952 return std::accumulate (children.rbegin(), children.rend(), static_cast<GList*>(nullptr),
2953 g_list_prepend);
2954}
2955
2956GList *
2958{
2959 g_return_val_if_fail(GNC_IS_ACCOUNT(account), nullptr);
2960 return g_list_sort(gnc_account_get_children (account), (GCompareFunc)xaccAccountOrder);
2961}
2962
2963gint
2965{
2966 g_return_val_if_fail(GNC_IS_ACCOUNT(account), 0);
2967 return GET_PRIVATE(account)->children.size();
2968}
2969
2970gint
2971gnc_account_child_index (const Account *parent, const Account *child)
2972{
2973 g_return_val_if_fail(GNC_IS_ACCOUNT(parent), -1);
2974 g_return_val_if_fail(GNC_IS_ACCOUNT(child), -1);
2975 auto& children = GET_PRIVATE(parent)->children;
2976 auto find_it = std::find (children.begin(), children.end(), child);
2977 return find_it == children.end() ? -1 : std::distance (children.begin(), find_it);
2978}
2979
2980Account *
2981gnc_account_nth_child (const Account *parent, gint num)
2982{
2983 g_return_val_if_fail(GNC_IS_ACCOUNT(parent), nullptr);
2984 if ((size_t)num >= GET_PRIVATE(parent)->children.size())
2985 return nullptr;
2986 return static_cast<Account*>(GET_PRIVATE(parent)->children.at (num));
2987}
2988
2989gint
2991{
2992 int count {0};
2993 gnc_account_foreach_descendant (account, [&count](auto acc){ count++; });
2994 return count;
2995}
2996
2997gint
2999{
3000 AccountPrivate *priv;
3001 int depth = 0;
3002
3003 g_return_val_if_fail(GNC_IS_ACCOUNT(account), 0);
3004
3005 priv = GET_PRIVATE(account);
3006 while (priv->parent && (priv->type != ACCT_TYPE_ROOT))
3007 {
3008 account = priv->parent;
3009 priv = GET_PRIVATE(account);
3010 depth++;
3011 }
3012
3013 return depth;
3014}
3015
3016gint
3018{
3019 AccountPrivate *priv;
3020 g_return_val_if_fail(GNC_IS_ACCOUNT(account), 0);
3021
3022 priv = GET_PRIVATE(account);
3023 if (!priv->children.size())
3024 return 1;
3025
3026 return 1 + std::accumulate (priv->children.begin(), priv->children.end(),
3027 0, [](auto a, auto b)
3028 { return std::max (a, gnc_account_get_tree_depth (b)); });
3029}
3030
3031GList *
3033{
3034 GList* list = nullptr;
3035 gnc_account_foreach_descendant (account, [&list](auto a){ list = g_list_prepend (list, a); });
3036 return g_list_reverse (list);
3037}
3038
3039GList *
3041{
3042 GList* list = nullptr;
3043 account_foreach_descendant_sorted (account, [&list](auto a){ list = g_list_prepend (list, a); });
3044 return g_list_reverse (list);
3045}
3046
3047// because gnc_account_lookup_by_name and gnc_account_lookup_by_code
3048// are described in Account.h searching breadth-first until 4.6, and
3049// accidentally modified to search depth-first from 4.7
3050// onwards. Restore breath-first searching in 4.11 onwards to match
3051// previous behaviour and function description in Account.h
3052static gpointer
3053account_foreach_descendant_breadthfirst_until (const Account *acc,
3054 AccountCb2 thunk,
3055 gpointer user_data)
3056{
3057 g_return_val_if_fail (GNC_IS_ACCOUNT(acc), nullptr);
3058 g_return_val_if_fail (thunk, nullptr);
3059
3060 auto& children{GET_PRIVATE(acc)->children};
3061
3062 for (auto acc : children)
3063 if (auto result = thunk (acc, user_data))
3064 return result;
3065
3066 for (auto acc: children)
3067 if (auto result = account_foreach_descendant_breadthfirst_until (acc, thunk, user_data))
3068 return result;
3069
3070 return nullptr;
3071}
3072
3073static gpointer
3074is_acct_name (Account *account, gpointer user_data)
3075{
3076 auto name {static_cast<gchar*>(user_data)};
3077 return (g_strcmp0 (name, xaccAccountGetName (account)) ? nullptr : account);
3078}
3079
3080Account *
3081gnc_account_lookup_by_name (const Account *parent, const char * name)
3082{
3083 return (Account*)account_foreach_descendant_breadthfirst_until (parent, is_acct_name, (char*)name);
3084}
3085
3086static gpointer
3087is_acct_code (Account *account, gpointer user_data)
3088{
3089 auto name {static_cast<gchar*>(user_data)};
3090 return (g_strcmp0 (name, xaccAccountGetCode (account)) ? nullptr : account);
3091}
3092
3093Account *
3094gnc_account_lookup_by_code (const Account *parent, const char * code)
3095{
3096 return (Account*)account_foreach_descendant_breadthfirst_until (parent, is_acct_code, (char*)code);
3097}
3098
3099static gpointer
3100is_opening_balance_account (Account* account, gpointer data)
3101{
3102 gnc_commodity* commodity = GNC_COMMODITY(data);
3104 return account;
3105 return nullptr;
3106}
3107
3108Account*
3109gnc_account_lookup_by_opening_balance (Account* account, gnc_commodity* commodity)
3110{
3111 return (Account *)gnc_account_foreach_descendant_until (account, is_opening_balance_account, commodity);
3112}
3113
3114/********************************************************************\
3115 * Fetch an account, given its full name *
3116\********************************************************************/
3117
3118static Account *
3119gnc_account_lookup_by_full_name_helper (const Account *parent,
3120 gchar **names)
3121{
3122 g_return_val_if_fail(GNC_IS_ACCOUNT(parent), nullptr);
3123 g_return_val_if_fail(names, nullptr);
3124
3125 /* Look for the first name in the children. */
3126 for (auto account : GET_PRIVATE(parent)->children)
3127 {
3128 auto priv = GET_PRIVATE(account);
3129 if (g_strcmp0(priv->accountName, names[0]) == 0)
3130 {
3131 /* We found an account. If the next entry is nullptr, there is
3132 * nothing left in the name, so just return the account. */
3133 if (names[1] == nullptr)
3134 return account;
3135
3136 /* No children? We're done. */
3137 if (priv->children.empty())
3138 return nullptr;
3139
3140 /* There's stuff left to search for. Search recursively. */
3141 if (auto found = gnc_account_lookup_by_full_name_helper(account, &names[1]))
3142 return found;
3143 }
3144 }
3145
3146 return nullptr;
3147}
3148
3149
3150Account *
3152 const gchar *name)
3153{
3154 const AccountPrivate *rpriv;
3155 const Account *root;
3156 Account *found;
3157 gchar **names;
3158
3159 g_return_val_if_fail(GNC_IS_ACCOUNT(any_acc), nullptr);
3160 g_return_val_if_fail(name, nullptr);
3161
3162 root = any_acc;
3163 rpriv = GET_PRIVATE(root);
3164 while (rpriv->parent)
3165 {
3166 root = rpriv->parent;
3167 rpriv = GET_PRIVATE(root);
3168 }
3169 names = g_strsplit(name, gnc_get_account_separator_string(), -1);
3170 found = gnc_account_lookup_by_full_name_helper(root, names);
3171 g_strfreev(names);
3172 return found;
3173}
3174
3175GList*
3177 const char* name,
3178 GNCAccountType acctype,
3179 gnc_commodity* commodity)
3180{
3181 GList *retval{};
3182 auto rpriv{GET_PRIVATE(root)};
3183 for (auto account : rpriv->children)
3184 {
3185 if (xaccAccountGetType (account) == acctype)
3186 {
3187 if (commodity &&
3189 commodity))
3190 continue;
3191
3192 if (name && strcmp(name, xaccAccountGetName(account)))
3193 continue;
3194
3195 retval = g_list_prepend(retval, account);
3196 }
3197 }
3198
3199 if (!retval) // Recurse through the children
3200 for (auto account : rpriv->children)
3201 {
3202 auto result = gnc_account_lookup_by_type_and_commodity(account,
3203 name,
3204 acctype,
3205 commodity);
3206 if (result)
3207 retval = g_list_concat(result, retval);
3208 }
3209 return retval;
3210}
3211
3212void
3214 AccountCb thunk,
3215 gpointer user_data)
3216{
3217 g_return_if_fail(GNC_IS_ACCOUNT(acc));
3218 g_return_if_fail(thunk);
3219 std::for_each (GET_PRIVATE(acc)->children.begin(), GET_PRIVATE(acc)->children.end(),
3220 [user_data, thunk](auto a){ thunk (a, user_data); });
3221}
3222
3223void
3224gnc_account_foreach_descendant (const Account *acc,
3225 AccountCb thunk,
3226 gpointer user_data)
3227{
3228 gnc_account_foreach_descendant (acc, [&](auto acc){ thunk (acc, user_data); });
3229}
3230
3231gpointer
3233 AccountCb2 thunk,
3234 gpointer user_data)
3235{
3236 gpointer result {nullptr};
3237
3238 g_return_val_if_fail (GNC_IS_ACCOUNT(acc), nullptr);
3239 g_return_val_if_fail (thunk, nullptr);
3240
3241 for (auto child : GET_PRIVATE(acc)->children)
3242 {
3243 result = thunk (child, user_data);
3244 if (result) break;
3245
3246 result = gnc_account_foreach_descendant_until (child, thunk, user_data);
3247 if (result) break;
3248 }
3249
3250 return result;
3251}
3252
3253
3256{
3257 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), ACCT_TYPE_NONE);
3258 return GET_PRIVATE(acc)->type;
3259}
3260
3261static const char*
3262qofAccountGetTypeString (const Account *acc)
3263{
3264 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
3265 return xaccAccountTypeEnumAsString(GET_PRIVATE(acc)->type);
3266}
3267
3268static void
3269qofAccountSetType (Account *acc, const char *type_string)
3270{
3271 g_return_if_fail(GNC_IS_ACCOUNT(acc));
3272 g_return_if_fail(type_string);
3274}
3275
3276const char *
3278{
3279 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
3280 return GET_PRIVATE(acc)->accountName;
3281}
3282
3283std::vector<const Account*>
3284gnc_account_get_all_parents (const Account *account)
3285{
3286 std::vector<const Account*> rv;
3287 for (auto a = account; !gnc_account_is_root (a); a = gnc_account_get_parent (a))
3288 rv.push_back (a);
3289 return rv;
3290}
3291
3292gchar *
3294{
3295 /* So much for hardening the API. Too many callers to this function don't
3296 * bother to check if they have a non-nullptr pointer before calling. */
3297 if (nullptr == account)
3298 return g_strdup("");
3299
3300 /* errors */
3301 g_return_val_if_fail(GNC_IS_ACCOUNT(account), g_strdup(""));
3302
3303 auto path{gnc_account_get_all_parents (account)};
3304 auto seps_size{path.empty() ? 0 : strlen (account_separator) * (path.size() - 1)};
3305 auto alloc_size{std::accumulate (path.begin(), path.end(), seps_size,
3306 [](auto sum, auto acc)
3307 { return sum + strlen (xaccAccountGetName (acc)); })};
3308 auto rv = g_new (char, alloc_size + 1);
3309 auto p = rv;
3310
3311 std::for_each (path.rbegin(), path.rend(),
3312 [&p, rv](auto a)
3313 {
3314 if (p != rv)
3315 p = stpcpy (p, account_separator);
3316 p = stpcpy (p, xaccAccountGetName (a));
3317 });
3318 *p = '\0';
3319
3320 return rv;
3321}
3322
3323const char *
3325{
3326 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
3327 return GET_PRIVATE(acc)->accountCode;
3328}
3329
3330const char *
3332{
3333 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
3334 return GET_PRIVATE(acc)->description;
3335}
3336
3337const char *
3339{
3340 return get_kvp_string_path (acc, {"color"});
3341}
3342
3343const char *
3345{
3346 return get_kvp_string_path (acc, {"filter"});
3347}
3348
3349const char *
3351{
3352 return get_kvp_string_path (acc, {"sort-order"});
3353}
3354
3355gboolean
3357{
3358 return get_kvp_boolean_path (acc, {"sort-reversed"});
3359}
3360
3361const char *
3363{
3364 return get_kvp_string_path (acc, {"notes"});
3365}
3366
3367const char *
3369{
3370 g_return_val_if_fail (GNC_IS_ACCOUNT(acc), nullptr);
3371 return get_kvp_string_path (acc, {KEY_ONLINE_ID});
3372}
3373
3374Account*
3375xaccAccountGetAssociatedAccount (const Account *acc, const char *tag)
3376{
3377 g_return_val_if_fail (tag && *tag, nullptr);
3378
3379 return get_kvp_account_path (acc, {"associated-account", tag});
3380}
3381
3382
3383gnc_commodity *
3385{
3386 if (auto s = get_kvp_string_path (acc, {"old-currency"}))
3387 {
3389 return gnc_commodity_table_lookup_unique (table, s);
3390 }
3391
3392 return nullptr;
3393}
3394
3395gnc_commodity *
3397{
3398 if (!GNC_IS_ACCOUNT(acc))
3399 return nullptr;
3400 return GET_PRIVATE(acc)->commodity;
3401}
3402
3403gnc_commodity * gnc_account_get_currency_or_parent(const Account* account)
3404{
3405 g_return_val_if_fail (GNC_IS_ACCOUNT (account), nullptr);
3406
3407 for (auto acc = account; acc; acc = gnc_account_get_parent (acc))
3408 if (auto comm = xaccAccountGetCommodity (acc); gnc_commodity_is_currency (comm))
3409 return comm;
3410
3411 return nullptr; // no suitable commodity found.
3412}
3413
3414/********************************************************************\
3415\********************************************************************/
3416void
3417gnc_account_set_start_balance (Account *acc, const gnc_numeric start_baln)
3418{
3419 AccountPrivate *priv;
3420
3421 g_return_if_fail(GNC_IS_ACCOUNT(acc));
3422
3423 priv = GET_PRIVATE(acc);
3424 priv->starting_balance = start_baln;
3425 priv->balance_dirty = TRUE;
3426}
3427
3428void
3430 const gnc_numeric start_baln)
3431{
3432 AccountPrivate *priv;
3433
3434 g_return_if_fail(GNC_IS_ACCOUNT(acc));
3435
3436 priv = GET_PRIVATE(acc);
3437 priv->starting_cleared_balance = start_baln;
3438 priv->balance_dirty = TRUE;
3439}
3440
3441void
3443 const gnc_numeric start_baln)
3444{
3445 AccountPrivate *priv;
3446
3447 g_return_if_fail(GNC_IS_ACCOUNT(acc));
3448
3449 priv = GET_PRIVATE(acc);
3450 priv->starting_reconciled_balance = start_baln;
3451 priv->balance_dirty = TRUE;
3452}
3453
3454gnc_numeric
3456{
3457 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
3458 return GET_PRIVATE(acc)->balance;
3459}
3460
3461gnc_numeric
3463{
3464 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
3465 return GET_PRIVATE(acc)->cleared_balance;
3466}
3467
3468gnc_numeric
3470{
3471 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
3472 return GET_PRIVATE(acc)->reconciled_balance;
3473}
3474
3475gnc_numeric
3476xaccAccountGetProjectedMinimumBalance (const Account *acc)
3477{
3478 auto today{gnc_time64_get_today_end()};
3479 std::optional<gnc_numeric> minimum;
3480
3481 auto before_today_end = [&minimum, today](const Split *s) -> bool
3482 {
3483 auto bal{xaccSplitGetBalance(s)};
3484 if (!minimum || gnc_numeric_compare (bal, *minimum) < 0)
3485 minimum = bal;
3486 return (xaccTransGetDate(xaccSplitGetParent(s)) < today);
3487 };
3488 // scan to find today's split, but we're really interested in the
3489 // minimum balance
3490 [[maybe_unused]] auto todays_split = gnc_account_find_split (acc, before_today_end, true);
3491 return minimum ? *minimum : gnc_numeric_zero();
3492}
3493
3494gboolean
3496{
3497 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), false);
3498 return GET_PRIVATE(acc)->has_stock_split;
3499}
3500
3501
3502/********************************************************************\
3503\********************************************************************/
3504
3505static gnc_numeric
3506GetBalanceAsOfDate (Account *acc, time64 date, std::function<gnc_numeric(Split*)> split_to_numeric)
3507{
3508 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
3509
3510 xaccAccountSortSplits (acc, TRUE); /* just in case, normally a noop */
3511 xaccAccountRecomputeBalance (acc); /* just in case, normally a noop */
3512
3513 auto is_before_date = [date](auto s) -> bool
3514 { return xaccTransGetDate(xaccSplitGetParent(s)) < date; };
3515
3516 auto latest_split{gnc_account_find_split (acc, is_before_date, true)};
3517 return latest_split ? split_to_numeric (latest_split) : gnc_numeric_zero();
3518}
3519
3520gnc_numeric
3522{
3523 return GetBalanceAsOfDate (acc, date, xaccSplitGetBalance);
3524}
3525
3526static gnc_numeric
3527xaccAccountGetNoclosingBalanceAsOfDate (Account *acc, time64 date)
3528{
3529 return GetBalanceAsOfDate (acc, date, xaccSplitGetNoclosingBalance);
3530}
3531
3532gnc_numeric
3534{
3535 return GetBalanceAsOfDate (acc, date, xaccSplitGetReconciledBalance);
3536}
3537
3538/*
3539 * Originally gsr_account_present_balance in gnc-split-reg.c
3540 */
3541gnc_numeric
3542xaccAccountGetPresentBalance (const Account *acc)
3543{
3544 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
3545
3546 return xaccAccountGetBalanceAsOfDate (GNC_ACCOUNT (acc),
3548}
3549
3550
3551/********************************************************************\
3552\********************************************************************/
3553/* XXX TODO: These 'GetBal' routines should be moved to some
3554 * utility area outside of the core account engine area.
3555 */
3556
3557/*
3558 * Convert a balance from one currency to another.
3559 */
3560gnc_numeric
3561xaccAccountConvertBalanceToCurrency(const Account *acc, /* for book */
3562 gnc_numeric balance,
3563 const gnc_commodity *balance_currency,
3564 const gnc_commodity *new_currency)
3565{
3566 QofBook *book;
3567 GNCPriceDB *pdb;
3568
3569 if (gnc_numeric_zero_p (balance) ||
3570 gnc_commodity_equiv (balance_currency, new_currency))
3571 return balance;
3572
3573 book = gnc_account_get_book (acc);
3574 pdb = gnc_pricedb_get_db (book);
3575
3577 pdb, balance, balance_currency, new_currency);
3578
3579 return balance;
3580}
3581
3582/*
3583 * Convert a balance from one currency to another with price of
3584 * a given date.
3585 */
3586gnc_numeric
3587xaccAccountConvertBalanceToCurrencyAsOfDate(const Account *acc, /* for book */
3588 gnc_numeric balance,
3589 const gnc_commodity *balance_currency,
3590 const gnc_commodity *new_currency,
3591 time64 date)
3592{
3593 QofBook *book;
3594 GNCPriceDB *pdb;
3595
3596 if (gnc_numeric_zero_p (balance) ||
3597 gnc_commodity_equiv (balance_currency, new_currency))
3598 return balance;
3599
3600 book = gnc_account_get_book (acc);
3601 pdb = gnc_pricedb_get_db (book);
3602
3604 pdb, balance, balance_currency, new_currency, date);
3605
3606 return balance;
3607}
3608
3609/*
3610 * Given an account and a GetBalanceFn pointer, extract the requested
3611 * balance from the account and then convert it to the desired
3612 * currency.
3613 */
3614static gnc_numeric
3615xaccAccountGetXxxBalanceInCurrency (const Account *acc,
3616 xaccGetBalanceFn fn,
3617 const gnc_commodity *report_currency)
3618{
3619 AccountPrivate *priv;
3620 gnc_numeric balance;
3621
3622 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
3623 g_return_val_if_fail(fn, gnc_numeric_zero());
3624 g_return_val_if_fail(GNC_IS_COMMODITY(report_currency), gnc_numeric_zero());
3625
3626 priv = GET_PRIVATE(acc);
3627 balance = fn(acc);
3628 balance = xaccAccountConvertBalanceToCurrency(acc, balance,
3629 priv->commodity,
3630 report_currency);
3631 return balance;
3632}
3633
3634static gnc_numeric
3635xaccAccountGetXxxBalanceAsOfDateInCurrency(Account *acc, time64 date,
3636 xaccGetBalanceAsOfDateFn fn,
3637 const gnc_commodity *report_commodity)
3638{
3639 AccountPrivate *priv;
3640
3641 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
3642 g_return_val_if_fail(fn, gnc_numeric_zero());
3643 g_return_val_if_fail(GNC_IS_COMMODITY(report_commodity), gnc_numeric_zero());
3644
3645 priv = GET_PRIVATE(acc);
3646 return xaccAccountConvertBalanceToCurrencyAsOfDate(
3647 acc, fn(acc, date), priv->commodity, report_commodity, date);
3648}
3649
3650/*
3651 * Data structure used to pass various arguments into the following fn.
3652 */
3653typedef struct
3654{
3655 const gnc_commodity *currency;
3656 gnc_numeric balance;
3657 xaccGetBalanceFn fn;
3658 xaccGetBalanceAsOfDateFn asOfDateFn;
3659 time64 date;
3661
3662
3663/*
3664 * A helper function for iterating over all the accounts in a list or
3665 * tree. This function is called once per account, and sums up the
3666 * values of all these accounts.
3667 */
3668static void
3669xaccAccountBalanceHelper (Account *acc, gpointer data)
3670{
3671 CurrencyBalance *cb = static_cast<CurrencyBalance*>(data);
3672 gnc_numeric balance;
3673
3674 if (!cb->fn || !cb->currency)
3675 return;
3676 balance = xaccAccountGetXxxBalanceInCurrency (acc, cb->fn, cb->currency);
3677 cb->balance = gnc_numeric_add (cb->balance, balance,
3678 gnc_commodity_get_fraction (cb->currency),
3679 GNC_HOW_RND_ROUND_HALF_UP);
3680}
3681
3682static void
3683xaccAccountBalanceAsOfDateHelper (Account *acc, gpointer data)
3684{
3685 CurrencyBalance *cb = static_cast<CurrencyBalance*>(data);
3686 gnc_numeric balance;
3687
3688 g_return_if_fail (cb->asOfDateFn && cb->currency);
3689
3690 balance = xaccAccountGetXxxBalanceAsOfDateInCurrency (
3691 acc, cb->date, cb->asOfDateFn, cb->currency);
3692 cb->balance = gnc_numeric_add (cb->balance, balance,
3693 gnc_commodity_get_fraction (cb->currency),
3694 GNC_HOW_RND_ROUND_HALF_UP);
3695}
3696
3697
3698
3699/*
3700 * Common function that iterates recursively over all accounts below
3701 * the specified account. It uses xaccAccountBalanceHelper to sum up
3702 * the balances of all its children, and uses the specified function
3703 * 'fn' for extracting the balance. This function may extract the
3704 * current value, the reconciled value, etc.
3705 *
3706 * If 'report_commodity' is nullptr, just use the account's commodity.
3707 * If 'include_children' is FALSE, this function doesn't recurse at all.
3708 */
3709static gnc_numeric
3710xaccAccountGetXxxBalanceInCurrencyRecursive (const Account *acc,
3711 xaccGetBalanceFn fn,
3712 const gnc_commodity *report_commodity,
3713 gboolean include_children)
3714{
3715 gnc_numeric balance;
3716
3717 if (!acc) return gnc_numeric_zero ();
3718 if (!report_commodity)
3719 report_commodity = xaccAccountGetCommodity (acc);
3720 if (!report_commodity)
3721 return gnc_numeric_zero();
3722
3723 balance = xaccAccountGetXxxBalanceInCurrency (acc, fn, report_commodity);
3724
3725 /* If needed, sum up the children converting to the *requested*
3726 commodity. */
3727 if (include_children)
3728 {
3729#ifdef _MSC_VER
3730 /* MSVC compiler: Somehow, the struct initialization containing a
3731 gnc_numeric doesn't work. As an exception, we hand-initialize
3732 that member afterwards. */
3733 CurrencyBalance cb = { report_commodity, { 0 }, fn, nullptr, 0 };
3734 cb.balance = balance;
3735#else
3736 CurrencyBalance cb = { report_commodity, balance, fn, nullptr, 0 };
3737#endif
3738
3739 gnc_account_foreach_descendant (acc, xaccAccountBalanceHelper, &cb);
3740 balance = cb.balance;
3741 }
3742
3743 return balance;
3744}
3745
3746static gnc_numeric
3747xaccAccountGetXxxBalanceAsOfDateInCurrencyRecursive (
3748 Account *acc, time64 date, xaccGetBalanceAsOfDateFn fn,
3749 const gnc_commodity *report_commodity, gboolean include_children)
3750{
3751 gnc_numeric balance;
3752
3753 g_return_val_if_fail(acc, gnc_numeric_zero());
3754 if (!report_commodity)
3755 report_commodity = xaccAccountGetCommodity (acc);
3756 if (!report_commodity)
3757 return gnc_numeric_zero();
3758
3759 balance = xaccAccountGetXxxBalanceAsOfDateInCurrency(
3760 acc, date, fn, report_commodity);
3761
3762 /* If needed, sum up the children converting to the *requested*
3763 commodity. */
3764 if (include_children)
3765 {
3766#ifdef _MSC_VER
3767 /* MSVC compiler: Somehow, the struct initialization containing a
3768 gnc_numeric doesn't work. As an exception, we hand-initialize
3769 that member afterwards. */
3770 CurrencyBalance cb = { report_commodity, 0, nullptr, fn, date };
3771 cb.balance = balance;
3772#else
3773 CurrencyBalance cb = { report_commodity, balance, nullptr, fn, date };
3774#endif
3775
3776 gnc_account_foreach_descendant (acc, xaccAccountBalanceAsOfDateHelper, &cb);
3777 balance = cb.balance;
3778 }
3779
3780 return balance;
3781}
3782
3783gnc_numeric
3784xaccAccountGetBalanceInCurrency (const Account *acc,
3785 const gnc_commodity *report_commodity,
3786 gboolean include_children)
3787{
3788 gnc_numeric rc;
3789 rc = xaccAccountGetXxxBalanceInCurrencyRecursive (
3790 acc, xaccAccountGetBalance, report_commodity, include_children);
3791 PINFO(" baln=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, rc.num, rc.denom);
3792 return rc;
3793}
3794
3795
3796gnc_numeric
3797xaccAccountGetClearedBalanceInCurrency (const Account *acc,
3798 const gnc_commodity *report_commodity,
3799 gboolean include_children)
3800{
3801 return xaccAccountGetXxxBalanceInCurrencyRecursive (
3802 acc, xaccAccountGetClearedBalance, report_commodity,
3803 include_children);
3804}
3805
3806gnc_numeric
3807xaccAccountGetReconciledBalanceInCurrency (const Account *acc,
3808 const gnc_commodity *report_commodity,
3809 gboolean include_children)
3810{
3811 return xaccAccountGetXxxBalanceInCurrencyRecursive (
3812 acc, xaccAccountGetReconciledBalance, report_commodity,
3813 include_children);
3814}
3815
3816gnc_numeric
3817xaccAccountGetPresentBalanceInCurrency (const Account *acc,
3818 const gnc_commodity *report_commodity,
3819 gboolean include_children)
3820{
3821 return xaccAccountGetXxxBalanceAsOfDateInCurrencyRecursive (
3823 report_commodity,
3824 include_children);
3825}
3826
3827gnc_numeric
3828xaccAccountGetProjectedMinimumBalanceInCurrency (
3829 const Account *acc,
3830 const gnc_commodity *report_commodity,
3831 gboolean include_children)
3832{
3833 return xaccAccountGetXxxBalanceInCurrencyRecursive (
3834 acc, xaccAccountGetProjectedMinimumBalance, report_commodity,
3835 include_children);
3836}
3837
3838gnc_numeric
3840 Account *acc, time64 date, gnc_commodity *report_commodity,
3841 gboolean include_children)
3842{
3843 return xaccAccountGetXxxBalanceAsOfDateInCurrencyRecursive (
3844 acc, date, xaccAccountGetBalanceAsOfDate, report_commodity,
3845 include_children);
3846}
3847
3848gnc_numeric
3850 Account *acc, time64 date, gnc_commodity *report_commodity,
3851 gboolean include_children)
3852{
3853 return xaccAccountGetXxxBalanceAsOfDateInCurrencyRecursive
3854 (acc, date, xaccAccountGetNoclosingBalanceAsOfDate,
3855 report_commodity, include_children);
3856}
3857
3858gnc_numeric
3859xaccAccountGetBalanceChangeForPeriod (Account *acc, time64 t1, time64 t2,
3860 gboolean recurse)
3861{
3862 gnc_numeric b1, b2;
3863
3864 b1 = xaccAccountGetBalanceAsOfDateInCurrency(acc, t1, nullptr, recurse);
3865 b2 = xaccAccountGetBalanceAsOfDateInCurrency(acc, t2, nullptr, recurse);
3866 return gnc_numeric_sub(b2, b1, GNC_DENOM_AUTO, GNC_HOW_DENOM_FIXED);
3867}
3868
3869gnc_numeric
3870xaccAccountGetNoclosingBalanceChangeForPeriod (Account *acc, time64 t1,
3871 time64 t2, gboolean recurse)
3872{
3873 gnc_numeric b1, b2;
3874
3875 b1 = xaccAccountGetNoclosingBalanceAsOfDateInCurrency(acc, t1, nullptr, recurse);
3876 b2 = xaccAccountGetNoclosingBalanceAsOfDateInCurrency(acc, t2, nullptr, recurse);
3877 return gnc_numeric_sub(b2, b1, GNC_DENOM_AUTO, GNC_HOW_DENOM_FIXED);
3878}
3879
3880typedef struct
3881{
3882 const gnc_commodity *currency;
3883 gnc_numeric balanceChange;
3884 time64 t1;
3885 time64 t2;
3887
3888static void
3889xaccAccountBalanceChangeHelper (Account *acc, gpointer data)
3890{
3891 CurrencyBalanceChange *cbdiff = static_cast<CurrencyBalanceChange*>(data);
3892
3893 gnc_numeric b1, b2;
3894 b1 = GetBalanceAsOfDate(acc, cbdiff->t1, xaccSplitGetNoclosingBalance);
3895 b2 = GetBalanceAsOfDate(acc, cbdiff->t2, xaccSplitGetNoclosingBalance);
3896 gnc_numeric balanceChange = gnc_numeric_sub(b2, b1, GNC_DENOM_AUTO, GNC_HOW_DENOM_FIXED);
3897 gnc_numeric balanceChange_conv = xaccAccountConvertBalanceToCurrencyAsOfDate(acc, balanceChange, xaccAccountGetCommodity(acc), cbdiff->currency, cbdiff->t2);
3898 cbdiff->balanceChange = gnc_numeric_add (cbdiff->balanceChange, balanceChange_conv,
3899 gnc_commodity_get_fraction (cbdiff->currency),
3900 GNC_HOW_RND_ROUND_HALF_UP);
3901}
3902
3903gnc_numeric
3904xaccAccountGetNoclosingBalanceChangeInCurrencyForPeriod (Account *acc, time64 t1,
3905 time64 t2, gboolean recurse)
3906{
3907
3908
3909 gnc_numeric b1, b2;
3910 b1 = GetBalanceAsOfDate(acc, t1, xaccSplitGetNoclosingBalance);
3911 b2 = GetBalanceAsOfDate(acc, t2, xaccSplitGetNoclosingBalance);
3912 gnc_numeric balanceChange = gnc_numeric_sub(b2, b1, GNC_DENOM_AUTO, GNC_HOW_DENOM_FIXED);
3913
3914 gnc_commodity *report_commodity = xaccAccountGetCommodity(acc);
3915 CurrencyBalanceChange cbdiff = { report_commodity, balanceChange, t1, t2 };
3916
3917 if(recurse)
3918 {
3919 gnc_account_foreach_descendant (acc, xaccAccountBalanceChangeHelper, &cbdiff);
3920 balanceChange = cbdiff.balanceChange;
3921 }
3922 return balanceChange;
3923}
3924
3925/********************************************************************\
3926\********************************************************************/
3927
3928const SplitsVec&
3929xaccAccountGetSplits (const Account *account)
3930{
3931 static const SplitsVec empty;
3932 g_return_val_if_fail (GNC_IS_ACCOUNT(account), empty);
3933 return GET_PRIVATE(account)->splits;
3934}
3935
3936SplitList *
3938{
3939 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
3940 auto priv{GET_PRIVATE(acc)};
3941 return std::accumulate (priv->splits.rbegin(), priv->splits.rend(),
3942 static_cast<GList*>(nullptr), g_list_prepend);
3943}
3944
3945size_t
3946xaccAccountGetSplitsSize (const Account *account)
3947{
3948 g_return_val_if_fail (GNC_IS_ACCOUNT(account), 0);
3949 return GNC_IS_ACCOUNT(account) ? GET_PRIVATE(account)->splits.size() : 0;
3950}
3951
3952gboolean
3953gnc_account_and_descendants_empty (Account *acc)
3954{
3955 g_return_val_if_fail (GNC_IS_ACCOUNT (acc), FALSE);
3956 auto priv = GET_PRIVATE (acc);
3957 if (!priv->splits.empty()) return FALSE;
3958 return std::all_of (priv->children.begin(), priv->children.end(),
3959 gnc_account_and_descendants_empty);
3960}
3961
3962LotList *
3964{
3965 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
3966 return g_list_copy(GET_PRIVATE(acc)->lots);
3967}
3968
3969LotList *
3971 gboolean (*match_func)(GNCLot *lot,
3972 gpointer user_data),
3973 gpointer user_data, GCompareFunc sort_func)
3974{
3975 AccountPrivate *priv;
3976 GList *lot_list;
3977 GList *retval = nullptr;
3978
3979 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
3980
3981 priv = GET_PRIVATE(acc);
3982 for (lot_list = priv->lots; lot_list; lot_list = lot_list->next)
3983 {
3984 GNCLot *lot = static_cast<GNCLot*>(lot_list->data);
3985
3986 /* If this lot is closed, then ignore it */
3987 if (gnc_lot_is_closed (lot))
3988 continue;
3989
3990 if (match_func && !(match_func)(lot, user_data))
3991 continue;
3992
3993 /* Ok, this is a valid lot. Add it to our list of lots */
3994 retval = g_list_prepend (retval, lot);
3995 }
3996
3997 if (sort_func)
3998 retval = g_list_sort (retval, sort_func);
3999
4000 return retval;
4001}
4002
4003gpointer
4004xaccAccountForEachLot(const Account *acc,
4005 gpointer (*proc)(GNCLot *lot, void *data), void *data)
4006{
4007 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
4008 g_return_val_if_fail(proc, nullptr);
4009
4010 for (auto node = GET_PRIVATE(acc)->lots; node; node = node->next)
4011 if (auto result = proc(GNC_LOT(node->data), data))
4012 return result;
4013
4014 return nullptr;
4015}
4016
4017
4018/********************************************************************\
4019\********************************************************************/
4020
4021/* These functions use interchange gint64 and gboolean. Is that right? */
4022gboolean
4024{
4025 return get_kvp_boolean_path(acc, {"tax-related"});
4026}
4027
4028void
4029xaccAccountSetTaxRelated (Account *acc, gboolean tax_related)
4030{
4031 set_kvp_boolean_path(acc, {"tax-related"}, tax_related);
4032}
4033
4034const char *
4036{
4037 return get_kvp_string_path (acc, {"tax-US", "code"});
4038}
4039
4040void
4041xaccAccountSetTaxUSCode (Account *acc, const char *code)
4042{
4043 set_kvp_string_path (acc, {"tax-US", "code"}, code);
4044}
4045
4046const char *
4048{
4049 return get_kvp_string_path (acc, {"tax-US", "payer-name-source"});
4050}
4051
4052void
4054{
4055 set_kvp_string_path (acc, {"tax-US", "payer-name-source"}, source);
4056}
4057
4058gint64
4060{
4061 auto copy_number = get_kvp_int64_path (acc, {"tax-US", "copy-number"});
4062 return (copy_number && (*copy_number != 0)) ? *copy_number : 1;
4063}
4064
4065void
4066xaccAccountSetTaxUSCopyNumber (Account *acc, gint64 copy_number)
4067{
4068 if (copy_number != 0)
4069 set_kvp_int64_path (acc, {"tax-US", "copy-number"}, copy_number);
4070 else
4071 /* deletes KVP if it exists */
4072 set_kvp_int64_path (acc, {"tax-US", "copy-number"}, std::nullopt);
4073}
4074
4075/*********************************************************************\
4076\ ********************************************************************/
4077
4078
4080{
4081 if (gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_ACCOUNTING_LABELS))
4082 return _(dflt_acct_debit_str);
4083
4084 auto result = gnc_acct_debit_strs.find(acct_type);
4085 if (result != gnc_acct_debit_strs.end())
4086 return _(result->second);
4087 else
4088 return _(dflt_acct_debit_str);
4089}
4090
4092{
4093 if (gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_ACCOUNTING_LABELS))
4094 return _(dflt_acct_credit_str);
4095
4096 auto result = gnc_acct_credit_strs.find(acct_type);
4097 if (result != gnc_acct_credit_strs.end())
4098 return _(result->second);
4099 else
4100 return _(dflt_acct_credit_str);
4101}
4102
4103/********************************************************************\
4104\********************************************************************/
4105
4106gboolean
4108{
4109 return get_kvp_boolean_path(acc, {"placeholder"});
4110}
4111
4112void
4114{
4115 set_kvp_boolean_path(acc, {"placeholder"}, val);
4116}
4117
4118gboolean
4120{
4121 return get_kvp_boolean_path(acc, {"import-append-text"});
4122}
4123
4124void
4126{
4127 set_kvp_boolean_path(acc, {"import-append-text"}, val);
4128}
4129
4130gboolean
4132{
4133 g_return_val_if_fail (GNC_IS_ACCOUNT(acc), false);
4134 if (GET_PRIVATE(acc)->type != ACCT_TYPE_EQUITY)
4135 return false;
4136
4137 return !g_strcmp0 (get_kvp_string_path (acc, {"equity-type"}), "opening-balance");
4138}
4139
4140void
4142{
4143 g_return_if_fail (GNC_IS_ACCOUNT(acc));
4144 if (GET_PRIVATE(acc)->type != ACCT_TYPE_EQUITY)
4145 return;
4146 set_kvp_string_path(acc, {"equity-type"}, val ? "opening-balance" : nullptr);
4147}
4148
4151{
4152 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), PLACEHOLDER_NONE);
4153 if (xaccAccountGetPlaceholder(acc)) return PLACEHOLDER_THIS;
4154
4155 return gnc_account_foreach_descendant_until (acc, (AccountCb2)xaccAccountGetPlaceholder, nullptr)
4156 ? PLACEHOLDER_CHILD : PLACEHOLDER_NONE;
4157}
4158
4159/********************************************************************\
4160 \********************************************************************/
4161
4162gboolean
4164{
4165 return get_kvp_boolean_path (acc, {KEY_RECONCILE_INFO, "auto-interest-transfer"});
4166}
4167
4168void
4170{
4171 set_kvp_boolean_path (acc, {KEY_RECONCILE_INFO, "auto-interest-transfer"}, val);
4172}
4173
4174/********************************************************************\
4175\********************************************************************/
4176
4177gboolean
4179{
4180 return get_kvp_boolean_path (acc, {"hidden"});
4181}
4182
4183void
4184xaccAccountSetHidden (Account *acc, gboolean val)
4185{
4186 set_kvp_boolean_path (acc, {"hidden"}, val);
4187}
4188
4189gboolean
4191{
4192 AccountPrivate *priv;
4193
4194 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
4195
4196 if (xaccAccountGetHidden(acc))
4197 return TRUE;
4198 priv = GET_PRIVATE(acc);
4199 while ((acc = priv->parent) != nullptr)
4200 {
4201 priv = GET_PRIVATE(acc);
4202 if (xaccAccountGetHidden(acc))
4203 return TRUE;
4204 }
4205 return FALSE;
4206}
4207
4208/********************************************************************\
4209\********************************************************************/
4210
4211gboolean
4212xaccAccountHasAncestor (const Account *acc, const Account * ancestor)
4213{
4214 const Account *parent;
4215
4216 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
4217 g_return_val_if_fail(GNC_IS_ACCOUNT(ancestor), FALSE);
4218
4219 parent = acc;
4220 while (parent && parent != ancestor)
4221 parent = GET_PRIVATE(parent)->parent;
4222
4223 return (parent == ancestor);
4224}
4225
4226/********************************************************************\
4227\********************************************************************/
4228
4229/* You must edit the functions in this block in tandem. KEEP THEM IN
4230 SYNC! */
4231
4232#define GNC_RETURN_ENUM_AS_STRING(x) case (ACCT_TYPE_ ## x): return #x;
4233
4234const char *
4236{
4237 switch (type)
4238 {
4239 GNC_RETURN_ENUM_AS_STRING(NONE);
4240 GNC_RETURN_ENUM_AS_STRING(BANK);
4241 GNC_RETURN_ENUM_AS_STRING(CASH);
4242 GNC_RETURN_ENUM_AS_STRING(CREDIT);
4243 GNC_RETURN_ENUM_AS_STRING(ASSET);
4244 GNC_RETURN_ENUM_AS_STRING(LIABILITY);
4245 GNC_RETURN_ENUM_AS_STRING(STOCK);
4246 GNC_RETURN_ENUM_AS_STRING(MUTUAL);
4247 GNC_RETURN_ENUM_AS_STRING(CURRENCY);
4248 GNC_RETURN_ENUM_AS_STRING(INCOME);
4249 GNC_RETURN_ENUM_AS_STRING(EXPENSE);
4250 GNC_RETURN_ENUM_AS_STRING(EQUITY);
4251 GNC_RETURN_ENUM_AS_STRING(RECEIVABLE);
4252 GNC_RETURN_ENUM_AS_STRING(PAYABLE);
4253 GNC_RETURN_ENUM_AS_STRING(ROOT);
4254 GNC_RETURN_ENUM_AS_STRING(TRADING);
4255 GNC_RETURN_ENUM_AS_STRING(CHECKING);
4256 GNC_RETURN_ENUM_AS_STRING(SAVINGS);
4257 GNC_RETURN_ENUM_AS_STRING(MONEYMRKT);
4258 GNC_RETURN_ENUM_AS_STRING(CREDITLINE);
4259 default:
4260 PERR ("asked to translate unknown account type %d.\n", type);
4261 break;
4262 }
4263 return(nullptr);
4264}
4265
4266#undef GNC_RETURN_ENUM_AS_STRING
4267
4268#define GNC_RETURN_ON_MATCH(x) \
4269 if(g_strcmp0(#x, (str)) == 0) { *type = ACCT_TYPE_ ## x; return(TRUE); }
4270
4271gboolean
4273{
4274
4275 GNC_RETURN_ON_MATCH(NONE);
4276 GNC_RETURN_ON_MATCH(BANK);
4277 GNC_RETURN_ON_MATCH(CASH);
4278 GNC_RETURN_ON_MATCH(CREDIT);
4279 GNC_RETURN_ON_MATCH(ASSET);
4280 GNC_RETURN_ON_MATCH(LIABILITY);
4281 GNC_RETURN_ON_MATCH(STOCK);
4282 GNC_RETURN_ON_MATCH(MUTUAL);
4283 GNC_RETURN_ON_MATCH(CURRENCY);
4284 GNC_RETURN_ON_MATCH(INCOME);
4285 GNC_RETURN_ON_MATCH(EXPENSE);
4286 GNC_RETURN_ON_MATCH(EQUITY);
4287 GNC_RETURN_ON_MATCH(RECEIVABLE);
4288 GNC_RETURN_ON_MATCH(PAYABLE);
4289 GNC_RETURN_ON_MATCH(ROOT);
4290 GNC_RETURN_ON_MATCH(TRADING);
4291 GNC_RETURN_ON_MATCH(CHECKING);
4292 GNC_RETURN_ON_MATCH(SAVINGS);
4293 GNC_RETURN_ON_MATCH(MONEYMRKT);
4294 GNC_RETURN_ON_MATCH(CREDITLINE);
4295
4296 PERR("asked to translate unknown account type string %s.\n",
4297 str ? str : "(null)");
4298
4299 return(FALSE);
4300}
4301
4302#undef GNC_RETURN_ON_MATCH
4303
4304/* impedance mismatch is a source of loss */
4307{
4308 GNCAccountType type;
4309 gboolean rc;
4310 rc = xaccAccountStringToType(str, &type);
4311 if (FALSE == rc) return ACCT_TYPE_INVALID;
4312 return type;
4313}
4314
4315/********************************************************************\
4316\********************************************************************/
4317
4318static char const *
4319account_type_name[NUM_ACCOUNT_TYPES] =
4320{
4321 N_("Bank"),
4322 N_("Cash"),
4323 N_("Asset"),
4324 N_("Credit Card"),
4325 N_("Liability"),
4326 N_("Stock"),
4327 N_("Mutual Fund"),
4328 N_("Currency"),
4329 N_("Income"),
4330 N_("Expense"),
4331 N_("Equity"),
4332 N_("A/Receivable"),
4333 N_("A/Payable"),
4334 N_("Root"),
4335 N_("Trading")
4336 /*
4337 N_("Checking"),
4338 N_("Savings"),
4339 N_("Money Market"),
4340 N_("Credit Line")
4341 */
4342};
4343
4344const char *
4346{
4347 if (type < 0 || NUM_ACCOUNT_TYPES <= type ) return "";
4348 return _(account_type_name [type]);
4349}
4350
4351/********************************************************************\
4352\********************************************************************/
4353
4354guint32
4356{
4357 switch (type)
4358 {
4359 case ACCT_TYPE_BANK:
4360 case ACCT_TYPE_CASH:
4361 case ACCT_TYPE_ASSET:
4362 case ACCT_TYPE_CREDIT:
4364 case ACCT_TYPE_INCOME:
4365 case ACCT_TYPE_EXPENSE:
4366 case ACCT_TYPE_EQUITY:
4367 return
4368 (1 << ACCT_TYPE_BANK) |
4369 (1 << ACCT_TYPE_CASH) |
4370 (1 << ACCT_TYPE_ASSET) |
4371 (1 << ACCT_TYPE_CREDIT) |
4372 (1 << ACCT_TYPE_LIABILITY) |
4373 (1 << ACCT_TYPE_INCOME) |
4374 (1 << ACCT_TYPE_EXPENSE) |
4375 (1 << ACCT_TYPE_EQUITY);
4376 case ACCT_TYPE_STOCK:
4377 case ACCT_TYPE_MUTUAL:
4378 case ACCT_TYPE_CURRENCY:
4379 return
4380 (1 << ACCT_TYPE_STOCK) |
4381 (1 << ACCT_TYPE_MUTUAL) |
4382 (1 << ACCT_TYPE_CURRENCY);
4384 return (1 << ACCT_TYPE_RECEIVABLE);
4385 case ACCT_TYPE_PAYABLE:
4386 return (1 << ACCT_TYPE_PAYABLE);
4387 case ACCT_TYPE_TRADING:
4388 return (1 << ACCT_TYPE_TRADING);
4389 default:
4390 PERR("bad account type: %d", type);
4391 return 0;
4392 }
4393}
4394guint32
4396{
4397 switch (type)
4398 {
4399 case ACCT_TYPE_BANK:
4400 case ACCT_TYPE_CASH:
4401 case ACCT_TYPE_ASSET:
4402 case ACCT_TYPE_STOCK:
4403 case ACCT_TYPE_MUTUAL:
4404 case ACCT_TYPE_CURRENCY:
4405 case ACCT_TYPE_CREDIT:
4408 case ACCT_TYPE_PAYABLE:
4409 return
4410 (1 << ACCT_TYPE_BANK) |
4411 (1 << ACCT_TYPE_CASH) |
4412 (1 << ACCT_TYPE_ASSET) |
4413 (1 << ACCT_TYPE_STOCK) |
4414 (1 << ACCT_TYPE_MUTUAL) |
4415 (1 << ACCT_TYPE_CURRENCY) |
4416 (1 << ACCT_TYPE_CREDIT) |
4417 (1 << ACCT_TYPE_LIABILITY) |
4418 (1 << ACCT_TYPE_RECEIVABLE) |
4419 (1 << ACCT_TYPE_PAYABLE) |
4420 (1 << ACCT_TYPE_ROOT);
4421 case ACCT_TYPE_INCOME:
4422 case ACCT_TYPE_EXPENSE:
4423 return
4424 (1 << ACCT_TYPE_INCOME) |
4425 (1 << ACCT_TYPE_EXPENSE) |
4426 (1 << ACCT_TYPE_ROOT);
4427 case ACCT_TYPE_EQUITY:
4428 return
4429 (1 << ACCT_TYPE_EQUITY) |
4430 (1 << ACCT_TYPE_ROOT);
4431 case ACCT_TYPE_TRADING:
4432 return
4433 (1 << ACCT_TYPE_TRADING) |
4434 (1 << ACCT_TYPE_ROOT);
4435 default:
4436 PERR("bad account type: %d", type);
4437 return 0;
4438 }
4439}
4440
4441gboolean
4443 GNCAccountType child_type)
4444{
4445 /* ACCT_TYPE_NONE isn't compatible with anything, even ACCT_TYPE_NONE. */
4446 if (parent_type == ACCT_TYPE_NONE || child_type == ACCT_TYPE_NONE)
4447 return FALSE;
4448
4449 /* ACCT_TYPE_ROOT can't have a parent account, and asking will raise
4450 * an error. */
4451 if (child_type == ACCT_TYPE_ROOT)
4452 return FALSE;
4453
4454 return ((xaccParentAccountTypesCompatibleWith (child_type) &
4455 (1 << parent_type))
4456 != 0);
4457}
4458
4459guint32
4461{
4462 guint32 mask = (1 << NUM_ACCOUNT_TYPES) - 1;
4463 mask &= ~((1 << ACCT_TYPE_CURRENCY) | /* DEPRECATED */
4464 (1 << ACCT_TYPE_ROOT)); /* ROOT */
4465
4466 return mask;
4467}
4468
4470{
4471 switch (t)
4472 {
4474 case ACCT_TYPE_PAYABLE:
4475 return FALSE;
4476 default:
4479 }
4480}
4481
4484{
4485 switch (t)
4486 {
4487 case ACCT_TYPE_BANK:
4488 case ACCT_TYPE_STOCK:
4490 case ACCT_TYPE_CHECKING:
4491 case ACCT_TYPE_SAVINGS:
4492 case ACCT_TYPE_MUTUAL:
4493 case ACCT_TYPE_CURRENCY:
4494 case ACCT_TYPE_CASH:
4495 case ACCT_TYPE_ASSET:
4497 return ACCT_TYPE_ASSET;
4498 case ACCT_TYPE_CREDIT:
4500 case ACCT_TYPE_PAYABLE:
4502 return ACCT_TYPE_LIABILITY;
4503 case ACCT_TYPE_INCOME:
4504 return ACCT_TYPE_INCOME;
4505 case ACCT_TYPE_EXPENSE:
4506 return ACCT_TYPE_EXPENSE;
4507 case ACCT_TYPE_EQUITY:
4508 return ACCT_TYPE_EQUITY;
4509 case ACCT_TYPE_TRADING:
4510 default:
4511 return ACCT_TYPE_NONE;
4512 }
4513}
4514
4516{
4517 switch (t)
4518 {
4520 case ACCT_TYPE_PAYABLE:
4521 return TRUE;
4522 default:
4523 return FALSE;
4524 }
4525}
4526
4528{
4529 switch (t)
4530 {
4531 case ACCT_TYPE_EQUITY:
4532 return TRUE;
4533 default:
4534 return FALSE;
4535 }
4536}
4537
4538gboolean
4540{
4541 AccountPrivate *priv;
4542
4543 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
4544
4545 priv = GET_PRIVATE(acc);
4546 return (priv->type == ACCT_TYPE_STOCK || priv->type == ACCT_TYPE_MUTUAL ||
4547 priv->type == ACCT_TYPE_CURRENCY);
4548}
4549
4550/********************************************************************\
4551\********************************************************************/
4552
4553gboolean
4555{
4556 gboolean retval = FALSE;
4557 auto date = get_kvp_int64_path (acc, {KEY_RECONCILE_INFO, "last-date"});
4558
4559 if (date)
4560 {
4561 if (last_date)
4562 *last_date = *date;
4563 retval = TRUE;
4564 }
4565 return retval;
4566}
4567
4568/********************************************************************\
4569\********************************************************************/
4570
4571void
4573{
4574 set_kvp_int64_path (acc, {KEY_RECONCILE_INFO, "last-date"}, last_date);
4575}
4576
4577/********************************************************************\
4578\********************************************************************/
4579
4580gboolean
4582 int *months, int *days)
4583{
4584 if (!acc) return FALSE;
4585 auto m{get_kvp_int64_path (acc, {KEY_RECONCILE_INFO, "last-interval", "months"})};
4586 auto d{get_kvp_int64_path (acc, {KEY_RECONCILE_INFO, "last-interval", "days"})};
4587 if (m && d)
4588 {
4589 if (months)
4590 *months = *m;
4591 if (days)
4592 *days = *d;
4593 return true;
4594 }
4595 return false;
4596}
4597
4598/********************************************************************\
4599\********************************************************************/
4600
4601void
4603{
4604 set_kvp_int64_path (acc, {KEY_RECONCILE_INFO, "last-interval", "months"}, months);
4605 set_kvp_int64_path (acc, {KEY_RECONCILE_INFO, "last-interval", "days"}, days);
4606}
4607
4608/********************************************************************\
4609\********************************************************************/
4610
4611gboolean
4613{
4614 if (auto date = get_kvp_int64_path (acc, {KEY_RECONCILE_INFO, KEY_POSTPONE, "date"}))
4615 {
4616 if (postpone_date)
4617 *postpone_date = *date;
4618 return true;
4619 }
4620 return false;
4621}
4622
4623/********************************************************************\
4624\********************************************************************/
4625
4626void
4628{
4629 set_kvp_int64_path (acc, {KEY_RECONCILE_INFO, KEY_POSTPONE, "date"}, postpone_date);
4630}
4631
4632/********************************************************************\
4633\********************************************************************/
4634
4635gboolean
4637 gnc_numeric *balance)
4638{
4639 if (auto bal = get_kvp_gnc_numeric_path (acc, {KEY_RECONCILE_INFO, KEY_POSTPONE, "balance"}))
4640 {
4641 if (balance)
4642 *balance = *bal;
4643 return true;
4644 }
4645 return false;
4646}
4647
4648/********************************************************************\
4649\********************************************************************/
4650
4651void
4653{
4654 set_kvp_gnc_numeric_path (acc, {KEY_RECONCILE_INFO, KEY_POSTPONE, "balance"}, balance);
4655}
4656
4657/********************************************************************\
4658
4659\********************************************************************/
4660
4661void
4663{
4664 set_kvp_gnc_numeric_path (acc, {KEY_RECONCILE_INFO, KEY_POSTPONE}, {});
4665}
4666
4667/********************************************************************\
4668\********************************************************************/
4669
4670const char *
4672{
4673 return get_kvp_string_path (acc, {"last-num"});
4674}
4675
4676/********************************************************************\
4677\********************************************************************/
4678
4679void
4680xaccAccountSetLastNum (Account *acc, const char *num)
4681{
4682 set_kvp_string_path (acc, {"last-num"}, num);
4683}
4684
4685
4686/********************************************************************\
4687\********************************************************************/
4688
4689static bool
4690get_balance_limit (const Account* acc, const std::string& key, gnc_numeric* balance)
4691{
4692 auto limit = get_kvp_gnc_numeric_path (acc, {KEY_BALANCE_LIMIT, key});
4693 if (limit)
4694 *balance = gnc_numeric_create (limit->num, limit->denom);
4695 return limit.has_value();
4696}
4697
4698static void
4699set_balance_limit (Account *acc, const std::string& key, std::optional<gnc_numeric> balance)
4700{
4701 if (balance && gnc_numeric_check (*balance))
4702 return;
4703 set_kvp_gnc_numeric_path (acc, {KEY_BALANCE_LIMIT, key}, balance);
4704}
4705
4706gboolean
4708 gnc_numeric *balance)
4709{
4710 return get_balance_limit (acc, KEY_BALANCE_HIGHER_LIMIT_VALUE, balance);
4711}
4712
4713gboolean
4715 gnc_numeric *balance)
4716{
4717 return get_balance_limit (acc, KEY_BALANCE_LOWER_LIMIT_VALUE, balance);
4718}
4719
4720void
4722{
4723 set_balance_limit (acc, KEY_BALANCE_HIGHER_LIMIT_VALUE, balance);
4724}
4725
4726void
4727xaccAccountSetLowerBalanceLimit (Account *acc, gnc_numeric balance)
4728{
4729 set_balance_limit (acc, KEY_BALANCE_LOWER_LIMIT_VALUE, balance);
4730}
4731
4732void
4734{
4735 set_balance_limit (acc, KEY_BALANCE_HIGHER_LIMIT_VALUE, {});
4736}
4737
4738void
4740{
4741 set_balance_limit (acc, KEY_BALANCE_LOWER_LIMIT_VALUE, {});
4742}
4743
4744gboolean
4746{
4747 return get_kvp_boolean_path (acc, {KEY_BALANCE_LIMIT, KEY_BALANCE_INCLUDE_SUB_ACCTS});
4748}
4749
4750void
4752{
4753 set_kvp_boolean_path (acc, {KEY_BALANCE_LIMIT, KEY_BALANCE_INCLUDE_SUB_ACCTS}, inc_sub);
4754}
4755
4756/********************************************************************\
4757\********************************************************************/
4758
4759static Account *
4760GetOrMakeOrphanAccount (Account *root, gnc_commodity * currency)
4761{
4762 char * accname;
4763 Account * acc;
4764
4765 g_return_val_if_fail (root, nullptr);
4766
4767 /* build the account name */
4768 if (!currency)
4769 {
4770 PERR ("No currency specified!");
4771 return nullptr;
4772 }
4773
4774 accname = g_strconcat (_("Orphaned Gains"), "-",
4775 gnc_commodity_get_mnemonic (currency), nullptr);
4776
4777 /* See if we've got one of these going already ... */
4778 acc = gnc_account_lookup_by_name(root, accname);
4779
4780 if (acc == nullptr)
4781 {
4782 /* Guess not. We'll have to build one. */
4783 acc = xaccMallocAccount (gnc_account_get_book(root));
4785 xaccAccountSetName (acc, accname);
4786 xaccAccountSetCommodity (acc, currency);
4787 xaccAccountSetType (acc, ACCT_TYPE_INCOME);
4788 xaccAccountSetDescription (acc, _("Realized Gain/Loss"));
4790 _("Realized Gains or Losses from "
4791 "Commodity or Trading Accounts "
4792 "that haven't been recorded elsewhere."));
4793
4794 /* Hang the account off the root. */
4795 gnc_account_append_child (root, acc);
4797 }
4798
4799 g_free (accname);
4800
4801 return acc;
4802}
4803
4804Account *
4805xaccAccountGainsAccount (Account *acc, gnc_commodity *curr)
4806{
4807 Path path {KEY_LOT_MGMT, "gains-acct", gnc_commodity_get_unique_name (curr)};
4808 auto gains_account = get_kvp_account_path (acc, path);
4809
4810 if (gains_account == nullptr) /* No gains account for this currency */
4811 {
4812 gains_account = GetOrMakeOrphanAccount (gnc_account_get_root (acc), curr);
4813 set_kvp_account_path (acc, path, gains_account);
4814 }
4815
4816 return gains_account;
4817}
4818
4819/********************************************************************\
4820\********************************************************************/
4821
4822void
4823dxaccAccountSetPriceSrc(Account *acc, const char *src)
4824{
4825 if (!acc) return;
4826
4827 if (xaccAccountIsPriced(acc))
4828 set_kvp_string_path (acc, {"old-price-source"}, src);
4829}
4830
4831/********************************************************************\
4832\********************************************************************/
4833
4834const char*
4836{
4837 if (!acc) return nullptr;
4838
4839 if (!xaccAccountIsPriced(acc)) return nullptr;
4840
4841 return get_kvp_string_path (acc, {"old-price-source"});
4842}
4843
4844/********************************************************************\
4845\********************************************************************/
4846
4847void
4848dxaccAccountSetQuoteTZ(Account *acc, const char *tz)
4849{
4850 if (!acc) return;
4851 if (!xaccAccountIsPriced(acc)) return;
4852 set_kvp_string_path (acc, {"old-quote-tz"}, tz);
4853}
4854
4855/********************************************************************\
4856\********************************************************************/
4857
4858const char*
4860{
4861 if (!acc) return nullptr;
4862 if (!xaccAccountIsPriced(acc)) return nullptr;
4863 return get_kvp_string_path (acc, {"old-quote-tz"});
4864}
4865
4866/********************************************************************\
4867\********************************************************************/
4868
4869void
4871{
4872 /* Would have been nice to use G_TYPE_BOOLEAN, but the other
4873 * boolean kvps save the value as "true" or "false" and that would
4874 * be file-incompatible with this.
4875 */
4876 set_kvp_int64_path (acc, {KEY_RECONCILE_INFO, KEY_INCLUDE_CHILDREN}, status);
4877}
4878
4879/********************************************************************\
4880\********************************************************************/
4881
4882gboolean
4884{
4885 /* access the account's kvp-data for status and return that, if no value
4886 * is found then we can assume not to include the children, that being
4887 * the default behaviour
4888 */
4889 return get_kvp_boolean_path (acc, {KEY_RECONCILE_INFO, KEY_INCLUDE_CHILDREN});
4890}
4891
4892/********************************************************************\
4893\********************************************************************/
4894
4895Split *
4896xaccAccountFindSplitByDesc(const Account *acc, const char *description)
4897{
4898 auto has_description = [description](const Split* s) -> bool
4899 { return !g_strcmp0 (description, xaccTransGetDescription (xaccSplitGetParent (s))); };
4900 return gnc_account_find_split (acc, has_description, true);
4901}
4902
4903/* This routine is for finding a matching transaction in an account by
4904 * matching on the description field. [CAS: The rest of this comment
4905 * seems to belong somewhere else.] This routine is used for
4906 * auto-filling in registers with a default leading account. The
4907 * dest_trans is a transaction used for currency checking. */
4908Transaction *
4909xaccAccountFindTransByDesc(const Account *acc, const char *description)
4910{
4911 auto split = xaccAccountFindSplitByDesc (acc, description);
4912 return split ? xaccSplitGetParent (split) : nullptr;
4913}
4914
4915/* ================================================================ */
4916/* Concatenation, Merging functions */
4917
4918void
4920{
4921
4922 /* errors */
4923 g_return_if_fail(GNC_IS_ACCOUNT(to_parent));
4924 g_return_if_fail(GNC_IS_ACCOUNT(from_parent));
4925
4926 /* optimizations */
4927 auto from_priv = GET_PRIVATE(from_parent);
4928 if (from_priv->children.empty())
4929 return;
4930
4931 ENTER (" ");
4932 auto children = from_priv->children;
4933 for (auto child : children)
4934 gnc_account_append_child(to_parent, child);
4935 LEAVE (" ");
4936}
4937/********************************************************************\
4938\********************************************************************/
4939
4940void
4942{
4943 g_return_if_fail(GNC_IS_ACCOUNT(parent));
4944
4945 auto ppriv = GET_PRIVATE(parent);
4946 for (auto it_a = ppriv->children.begin(); it_a != ppriv->children.end(); it_a++)
4947 {
4948 auto acc_a = *it_a;
4949 auto priv_a = GET_PRIVATE(acc_a);
4950 for (auto it_b = std::next(it_a); it_b != ppriv->children.end(); it_b++)
4951 {
4952 auto acc_b = *it_b;
4953 auto priv_b = GET_PRIVATE(acc_b);
4954 if (0 != null_strcmp(priv_a->accountName, priv_b->accountName))
4955 continue;
4956 if (0 != null_strcmp(priv_a->accountCode, priv_b->accountCode))
4957 continue;
4958 if (0 != null_strcmp(priv_a->description, priv_b->description))
4959 continue;
4960 if (0 != null_strcmp(xaccAccountGetColor(acc_a),
4961 xaccAccountGetColor(acc_b)))
4962 continue;
4963 if (!gnc_commodity_equiv(priv_a->commodity, priv_b->commodity))
4964 continue;
4965 if (0 != null_strcmp(xaccAccountGetNotes(acc_a),
4966 xaccAccountGetNotes(acc_b)))
4967 continue;
4968 if (priv_a->type != priv_b->type)
4969 continue;
4970
4971 /* consolidate children */
4972 if (!priv_b->children.empty())
4973 {
4974 auto work = priv_b->children;
4975 for (auto w : work)
4976 gnc_account_append_child (acc_a, w);
4977
4978 qof_event_gen (&acc_a->inst, QOF_EVENT_MODIFY, nullptr);
4979 qof_event_gen (&acc_b->inst, QOF_EVENT_MODIFY, nullptr);
4980 }
4981
4982 /* recurse to do the children's children */
4984
4985 /* consolidate transactions */
4986 while (!priv_b->splits.empty())
4987 xaccSplitSetAccount (priv_b->splits.front(), acc_a);
4988
4989 /* move back one before removal. next iteration around the loop
4990 * will get the node after node_b */
4991 it_b--;
4992
4993 /* The destroy function will remove from list -- node_a is ok,
4994 * it's before node_b */
4995 xaccAccountBeginEdit (acc_b);
4996 xaccAccountDestroy (acc_b);
4997 }
4998 }
4999}
5000
5001/* ================================================================ */
5002/* Transaction Traversal functions */
5003
5004
5005static void
5006xaccSplitsBeginStagedTransactionTraversals (SplitsVec& splits)
5007{
5008 for (auto s : splits)
5009 {
5010 Transaction *trans = s->parent;
5011
5012 if (trans)
5013 trans->marker = 0;
5014 }
5015}
5016
5017/* original function */
5018void
5020{
5021 if (!account)
5022 return;
5023 xaccSplitsBeginStagedTransactionTraversals(GET_PRIVATE (account)->splits);
5024}
5025
5026gboolean
5027xaccTransactionTraverse (Transaction *trans, int stage)
5028{
5029 if (trans == nullptr) return FALSE;
5030
5031 if (trans->marker < stage)
5032 {
5033 trans->marker = stage;
5034 return TRUE;
5035 }
5036
5037 return FALSE;
5038}
5039
5040/* Replacement for xaccGroupBeginStagedTransactionTraversals */
5041void
5043{
5044 auto do_one_account = [](auto acc)
5045 { gnc_account_foreach_split (acc, [](auto s){ s->parent->marker = 0; }); };
5046 gnc_account_foreach_descendant (account, do_one_account);
5047}
5048
5049int
5051 unsigned int stage,
5052 TransactionCallback thunk,
5053 void *cb_data)
5054{
5055 if (!acc) return 0;
5056
5057 // iterate on copy of splits. some callers modify the splitsvec.
5058 auto splits = GET_PRIVATE(acc)->splits;
5059 for (auto s : splits)
5060 {
5061 auto trans = s->parent;
5062 if (trans && (trans->marker < stage))
5063 {
5064 trans->marker = stage;
5065 if (thunk)
5066 {
5067 auto retval = thunk(trans, cb_data);
5068 if (retval) return retval;
5069 }
5070 }
5071 }
5072
5073 return 0;
5074}
5075
5076int
5078 unsigned int stage,
5079 TransactionCallback thunk,
5080 void *cb_data)
5081{
5082 const AccountPrivate *priv;
5083 Transaction *trans;
5084 int retval;
5085
5086 if (!acc) return 0;
5087
5088 /* depth first traversal */
5089 priv = GET_PRIVATE(acc);
5090 for (auto acc_p : priv->children)
5091 {
5092 retval = gnc_account_tree_staged_transaction_traversal(acc_p, stage, thunk, cb_data);
5093 if (retval) return retval;
5094 }
5095
5096 /* Now this account */
5097 for (auto s : priv->splits)
5098 {
5099 trans = s->parent;
5100 if (trans && (trans->marker < stage))
5101 {
5102 trans->marker = stage;
5103 if (thunk)
5104 {
5105 retval = thunk(trans, cb_data);
5106 if (retval) return retval;
5107 }
5108 }
5109 }
5110
5111 return 0;
5112}
5113
5114time64
5116{
5117 g_return_val_if_fail (GNC_IS_ACCOUNT(account), INT64_MAX);
5118 const auto& splits = xaccAccountGetSplits (account);
5119 return splits.empty() ? INT64_MAX : xaccTransGetDate (xaccSplitGetParent (splits.front()));
5120}
5121
5122/********************************************************************\
5123\********************************************************************/
5124
5125int
5126xaccAccountTreeForEachTransaction (Account *acc,
5127 int (*proc)(Transaction *t, void *data),
5128 void *data)
5129{
5130 if (!acc || !proc) return 0;
5131
5133 return gnc_account_tree_staged_transaction_traversal (acc, 42, proc, data);
5134}
5135
5136
5137gint
5138xaccAccountForEachTransaction(const Account *acc, TransactionCallback proc,
5139 void *data)
5140{
5141 if (!acc || !proc) return 0;
5143 return xaccAccountStagedTransactionTraversal(acc, 42, proc, data);
5144}
5145
5146/* ================================================================ */
5147/* The following functions are used by
5148 * src/import-export/import-backend.c to manipulate the contra-account
5149 * matching data. See src/import-export/import-backend.c for explanations.
5150 */
5151
5152#define IMAP_FRAME "import-map"
5153#define IMAP_FRAME_BAYES "import-map-bayes"
5154
5155/* Look up an Account in the map */
5156Account*
5157gnc_account_imap_find_account (Account *acc,
5158 const char *category,
5159 const char *key)
5160{
5161 if (!acc || !key) return nullptr;
5162 std::vector<std::string> path {IMAP_FRAME};
5163 if (category)
5164 path.push_back (category);
5165 path.push_back (key);
5166 return get_kvp_account_path (acc, path);
5167}
5168
5169Account*
5170gnc_account_imap_find_any (QofBook *book, const char* category, const char *key)
5171{
5172 Account *account = nullptr;
5173
5174 /* Get list of Accounts */
5175 auto root = gnc_book_get_root_account (book);
5176 auto accts = gnc_account_get_descendants_sorted (root);
5177
5178 /* Go through list of accounts */
5179 for (auto ptr = accts; ptr; ptr = g_list_next (ptr))
5180 {
5181 auto tmp_acc = static_cast<Account*> (ptr->data);
5182
5183 if (gnc_account_imap_find_account (tmp_acc, category, key))
5184 {
5185 account = tmp_acc;
5186 break;
5187 }
5188 }
5189 g_list_free (accts);
5190
5191return account;
5192}
5193
5194/* Store an Account in the map */
5195void
5196gnc_account_imap_add_account (Account *acc,
5197 const char *category,
5198 const char *key,
5199 Account *added_acc)
5200{
5201 if (!acc || !key || !added_acc || !*key) return;
5202
5203 auto path = category ? Path{IMAP_FRAME, category, key} : Path{IMAP_FRAME, key};
5204
5205 set_kvp_account_path (acc, path, added_acc);
5206}
5207
5208/* Remove a reference to an Account in the map */
5209void
5210gnc_account_imap_delete_account (Account *acc,
5211 const char *category,
5212 const char *key)
5213{
5214 if (!acc || !key) return;
5215
5216 auto path = category ? Path{IMAP_FRAME, category, key} : Path{IMAP_FRAME, key};
5217 if (qof_instance_has_path_slot (QOF_INSTANCE (acc), path))
5218 {
5219 qof_instance_slot_path_delete (QOF_INSTANCE (acc), path);
5220 if (category)
5221 qof_instance_slot_path_delete_if_empty (QOF_INSTANCE (acc), {IMAP_FRAME, category});
5222 qof_instance_slot_path_delete_if_empty (QOF_INSTANCE (acc), {IMAP_FRAME});
5223 }
5224 qof_instance_set_dirty (QOF_INSTANCE (acc));
5225}
5226
5227/*--------------------------------------------------------------------------
5228 Below here is the bayes transaction to account matching system
5229--------------------------------------------------------------------------*/
5230
5231
5237{
5238 double product; /* product of probabilities */
5239 double product_difference; /* product of (1-probabilities) */
5240};
5241
5243{
5244 std::string account_guid;
5245 int64_t token_count;
5246};
5247
5252{
5253 std::vector<AccountTokenCount> accounts;
5254 int64_t total_count;
5255};
5256
5261{
5262 std::string account_guid;
5263 int32_t probability;
5264};
5265
5266static void
5267build_token_info(char const * suffix, KvpValue * value, TokenAccountsInfo & tokenInfo)
5268{
5269 if (strlen(suffix) == GUID_ENCODING_LENGTH)
5270 {
5271 tokenInfo.total_count += value->get<int64_t>();
5272 /*By convention, the key ends with the account GUID.*/
5273 tokenInfo.accounts.emplace_back(AccountTokenCount{std::string{suffix}, value->get<int64_t>()});
5274 }
5275}
5276
5280static constexpr int probability_factor = 100000;
5281
5282static FinalProbabilityVec
5283build_probabilities(ProbabilityVec const & first_pass)
5284{
5285 FinalProbabilityVec ret;
5286 for (auto const & first_pass_prob : first_pass)
5287 {
5288 auto const & account_probability = first_pass_prob.second;
5289 /* P(AB) = A*B / [A*B + (1-A)*(1-B)]
5290 * NOTE: so we only keep track of a running product(A*B*C...)
5291 * and product difference ((1-A)(1-B)...)
5292 */
5293 int32_t probability = (account_probability.product /
5294 (account_probability.product + account_probability.product_difference)) * probability_factor;
5295 ret.push_back({first_pass_prob.first, probability});
5296 }
5297 return ret;
5298}
5299
5300static AccountInfo
5301highest_probability(FinalProbabilityVec const & probabilities)
5302{
5303 AccountInfo ret {"", std::numeric_limits<int32_t>::min()};
5304 for (auto const & prob : probabilities)
5305 if (prob.second > ret.probability)
5306 ret = AccountInfo {prob.first, prob.second};
5307 return ret;
5308}
5309
5310static ProbabilityVec
5311get_first_pass_probabilities(Account* acc, GList * tokens)
5312{
5313 ProbabilityVec ret;
5314 /* find the probability for each account that contains any of the tokens
5315 * in the input tokens list. */
5316 for (auto current_token = tokens; current_token; current_token = current_token->next)
5317 {
5318 TokenAccountsInfo tokenInfo{};
5319 auto path = std::string{IMAP_FRAME_BAYES "/"} + static_cast <char const *> (current_token->data) + "/";
5320 qof_instance_foreach_slot_prefix (QOF_INSTANCE (acc), path, &build_token_info, tokenInfo);
5321 for (auto const & current_account_token : tokenInfo.accounts)
5322 {
5323 auto item = std::find_if(ret.begin(), ret.end(), [&current_account_token]
5324 (std::pair<std::string, AccountProbability> const & a) {
5325 return current_account_token.account_guid == a.first;
5326 });
5327 if (item != ret.end())
5328 {/* This account is already in the map */
5329 item->second.product = ((double)current_account_token.token_count /
5330 (double)tokenInfo.total_count) * item->second.product;
5331 item->second.product_difference = ((double)1 - ((double)current_account_token.token_count /
5332 (double)tokenInfo.total_count)) * item->second.product_difference;
5333 }
5334 else
5335 {
5336 /* add a new entry */
5337 AccountProbability new_probability;
5338 new_probability.product = ((double)current_account_token.token_count /
5339 (double)tokenInfo.total_count);
5340 new_probability.product_difference = 1 - (new_probability.product);
5341 ret.push_back({current_account_token.account_guid, std::move(new_probability)});
5342 }
5343 } /* for all accounts in tokenInfo */
5344 }
5345 return ret;
5346}
5347
5348static std::string
5349look_for_old_separator_descendants (Account *root, std::string const & full_name, const gchar *separator)
5350{
5351 GList *top_accounts, *ptr;
5352 gint found_len = 0;
5353 gchar found_sep;
5354 top_accounts = gnc_account_get_descendants (root);
5355 PINFO("Incoming full_name is '%s', current separator is '%s'", full_name.c_str (), separator);
5356 /* Go through list of top level accounts */
5357 for (ptr = top_accounts; ptr; ptr = g_list_next (ptr))
5358 {
5359 const gchar *name = xaccAccountGetName (static_cast <Account const *> (ptr->data));
5360 // we are looking for the longest top level account that matches
5361 if (g_str_has_prefix (full_name.c_str (), name))
5362 {
5363 gint name_len = strlen (name);
5364 const gchar old_sep = full_name[name_len];
5365 if (!g_ascii_isalnum (old_sep)) // test for non alpha numeric
5366 {
5367 if (name_len > found_len)
5368 {
5369 found_sep = full_name[name_len];
5370 found_len = name_len;
5371 }
5372 }
5373 }
5374 }
5375 g_list_free (top_accounts); // Free the List
5376 std::string new_name {full_name};
5377 if (found_len > 1)
5378 std::replace (new_name.begin (), new_name.end (), found_sep, *separator);
5379 PINFO ("Return full_name is '%s'", new_name.c_str ());
5380 return new_name;
5381}
5382
5383static std::string
5384get_guid_from_account_name (Account * root, std::string const & name)
5385{
5386 auto map_account = gnc_account_lookup_by_full_name (root, name.c_str ());
5387 if (!map_account)
5388 {
5389 auto temp_account_name = look_for_old_separator_descendants (root, name,
5391 map_account = gnc_account_lookup_by_full_name (root, temp_account_name.c_str ());
5392 }
5393 auto temp_guid = gnc::GUID {*xaccAccountGetGUID (map_account)};
5394 return temp_guid.to_string ();
5395}
5396
5397static FlatKvpEntry
5398convert_entry (KvpEntry entry, Account* root)
5399{
5400 /*We need to make a copy here.*/
5401 auto account_name = entry.first.back();
5402 if (!gnc::GUID::is_valid_guid (account_name))
5403 {
5404 /* Earlier version stored the account name in the import map, and
5405 * there were early beta versions of 2.7 that stored a GUID.
5406 * If there is no GUID, we assume it's an account name. */
5407 /* Take off the account name and replace it with the GUID */
5408 entry.first.pop_back();
5409 auto guid_str = get_guid_from_account_name (root, account_name);
5410 entry.first.emplace_back (guid_str);
5411 }
5412 std::string new_key {std::accumulate (entry.first.begin(), entry.first.end(), std::string {})};
5413 new_key = IMAP_FRAME_BAYES + new_key;
5414 return {new_key, entry.second};
5415}
5416
5417static std::vector<FlatKvpEntry>
5418get_flat_imap (Account * acc)
5419{
5420 auto frame = qof_instance_get_slots (QOF_INSTANCE (acc));
5421 auto slot = frame->get_slot ({IMAP_FRAME_BAYES});
5422 if (!slot)
5423 return {};
5424 auto imap_frame = slot->get<KvpFrame*> ();
5425 auto flat_kvp = imap_frame->flatten_kvp ();
5426 auto root = gnc_account_get_root (acc);
5427 std::vector <FlatKvpEntry> ret;
5428 for (auto const & flat_entry : flat_kvp)
5429 {
5430 auto converted_entry = convert_entry (flat_entry, root);
5431 /*If the entry was invalid, we don't perpetuate it.*/
5432 if (converted_entry.first.size())
5433 ret.emplace_back (converted_entry);
5434 }
5435 return ret;
5436}
5437
5438static bool
5439convert_imap_account_bayes_to_flat (Account *acc)
5440{
5441 auto frame = qof_instance_get_slots (QOF_INSTANCE (acc));
5442 if (!frame->get_keys().size())
5443 return false;
5444 auto flat_imap = get_flat_imap(acc);
5445 if (!flat_imap.size ())
5446 return false;
5448 frame->set({IMAP_FRAME_BAYES}, nullptr);
5449 std::for_each(flat_imap.begin(), flat_imap.end(),
5450 [&frame] (FlatKvpEntry const & entry) {
5451 frame->set({entry.first.c_str()}, entry.second);
5452 });
5453 qof_instance_set_dirty (QOF_INSTANCE (acc));
5455 return true;
5456}
5457
5458/*
5459 * Checks for import map data and converts them when found.
5460 */
5461static bool
5462imap_convert_bayes_to_flat (QofBook * book)
5463{
5464 auto root = gnc_book_get_root_account (book);
5465 auto accts = gnc_account_get_descendants_sorted (root);
5466 bool ret = false;
5467 for (auto ptr = accts; ptr; ptr = g_list_next (ptr))
5468 {
5469 Account *acc = static_cast <Account*> (ptr->data);
5470 if (convert_imap_account_bayes_to_flat (acc))
5471 {
5472 ret = true;
5473 gnc_features_set_used (book, GNC_FEATURE_GUID_FLAT_BAYESIAN);
5474 }
5475 }
5476 g_list_free (accts);
5477 return ret;
5478}
5479
5480void
5482{
5483 imap_convert_bayes_to_flat_run = false;
5484}
5485
5486/*
5487 * Here we check to see the state of import map data.
5488 *
5489 * If the GUID_FLAT_BAYESIAN feature flag is set, everything
5490 * should be fine.
5491 *
5492 * If it is not set, there are two possibilities: import data
5493 * are present from a previous version or not. If they are,
5494 * they are converted, and the feature flag set. If there are
5495 * no previous data, nothing is done.
5496 */
5497static void
5498check_import_map_data (QofBook *book)
5499{
5500 if (gnc_features_check_used (book, GNC_FEATURE_GUID_FLAT_BAYESIAN) ||
5501 imap_convert_bayes_to_flat_run)
5502 return;
5503
5504 /* This function will set GNC_FEATURE_GUID_FLAT_BAYESIAN if necessary.*/
5505 imap_convert_bayes_to_flat (book);
5506 imap_convert_bayes_to_flat_run = true;
5507}
5508
5509static constexpr double threshold = .90 * probability_factor; /* 90% */
5510
5512Account*
5514{
5515 if (!acc)
5516 return nullptr;
5517 auto book = gnc_account_get_book(acc);
5518 check_import_map_data (book);
5519 auto first_pass = get_first_pass_probabilities(acc, tokens);
5520 if (!first_pass.size())
5521 return nullptr;
5522 auto final_probabilities = build_probabilities(first_pass);
5523 if (!final_probabilities.size())
5524 return nullptr;
5525 auto best = highest_probability(final_probabilities);
5526 if (best.account_guid == "")
5527 return nullptr;
5528 if (best.probability < threshold)
5529 return nullptr;
5530 gnc::GUID guid;
5531 try {
5532 guid = gnc::GUID::from_string(best.account_guid);
5533 } catch (gnc::guid_syntax_exception&) {
5534 return nullptr;
5535 }
5536 auto account = xaccAccountLookup (reinterpret_cast<GncGUID*>(&guid), book);
5537 return account;
5538}
5539
5540static void
5541change_imap_entry (Account *acc, std::string const & path, int64_t token_count)
5542{
5543 PINFO("Source Account is '%s', Count is '%" G_GINT64_FORMAT "'",
5544 xaccAccountGetName (acc), token_count);
5545
5546 // check for existing guid entry
5547 if (auto existing_token_count = get_kvp_int64_path (acc, {path}))
5548 {
5549 PINFO("found existing value of '%" G_GINT64_FORMAT "'", *existing_token_count);
5550 token_count += *existing_token_count;
5551 }
5552
5553 // Add or Update the entry based on guid
5554 set_kvp_int64_path (acc, {path}, token_count);
5555}
5556
5558void
5560 GList *tokens,
5561 Account *added_acc)
5562{
5563 GList *current_token;
5564 gint64 token_count;
5565 char *account_fullname;
5566 char *guid_string;
5567
5568 ENTER(" ");
5569 if (!acc)
5570 {
5571 LEAVE(" ");
5572 return;
5573 }
5574 check_import_map_data (gnc_account_get_book(acc));
5575
5576 g_return_if_fail (added_acc != nullptr);
5577 account_fullname = gnc_account_get_full_name(added_acc);
5579
5580 PINFO("account name: '%s'", account_fullname);
5581
5582 guid_string = guid_to_string (xaccAccountGetGUID (added_acc));
5583
5584 /* process each token in the list */
5585 for (current_token = g_list_first(tokens); current_token;
5586 current_token = current_token->next)
5587 {
5588 char* token = static_cast<char*>(current_token->data);
5589 /* Jump to next iteration if the pointer is not valid or if the
5590 string is empty. In HBCI import we almost always get an empty
5591 string, which doesn't work in the kvp loopkup later. So we
5592 skip this case here. */
5593 if (!token || !token[0])
5594 continue;
5595 /* start off with one token for this account */
5596 token_count = 1;
5597 PINFO("adding token '%s'", token);
5598 auto path = std::string {IMAP_FRAME_BAYES} + '/' + token + '/' + guid_string;
5599 /* change the imap entry for the account */
5600 change_imap_entry (acc, path, token_count);
5601 }
5602 /* free up the account fullname and guid string */
5604 gnc_features_set_used (gnc_account_get_book(acc), GNC_FEATURE_GUID_FLAT_BAYESIAN);
5605 g_free (account_fullname);
5606 g_free (guid_string);
5607 LEAVE(" ");
5608}
5609
5610/*******************************************************************************/
5611
5612static void
5613build_non_bayes (const char *key, const GValue *value, gpointer user_data)
5614{
5615 if (!G_VALUE_HOLDS_BOXED (value))
5616 return;
5617 QofBook *book;
5618 GncGUID *guid = nullptr;
5619 gchar *guid_string = nullptr;
5620 auto imapInfo = (GncImapInfo*)user_data;
5621 // Get the book
5622 book = qof_instance_get_book (imapInfo->source_account);
5623
5624 guid = (GncGUID*)g_value_get_boxed (value);
5625 guid_string = guid_to_string (guid);
5626
5627 PINFO("build_non_bayes: match string '%s', match account guid: '%s'",
5628 (char*)key, guid_string);
5629
5630 auto imapInfo_node = static_cast <GncImapInfo*> (g_malloc(sizeof(GncImapInfo)));
5631
5632 imapInfo_node->source_account = imapInfo->source_account;
5633 imapInfo_node->map_account = xaccAccountLookup (guid, book);
5634 imapInfo_node->head = g_strdup (imapInfo->head);
5635 imapInfo_node->match_string = g_strdup (key);
5636 imapInfo_node->category = g_strdup (imapInfo->category);
5637 imapInfo_node->count = g_strdup (" ");
5638
5639 imapInfo->list = g_list_prepend (imapInfo->list, imapInfo_node);
5640
5641 g_free (guid_string);
5642}
5643
5644static void
5645build_bayes (const char *suffix, KvpValue * value, GncImapInfo & imapInfo)
5646{
5647 size_t guid_start = strlen(suffix) - GUID_ENCODING_LENGTH;
5648 std::string account_guid {&suffix[guid_start]};
5649 GncGUID guid;
5650 try
5651 {
5652 guid = gnc::GUID::from_string (account_guid);
5653 }
5654 catch (const gnc::guid_syntax_exception& err)
5655 {
5656 PWARN("Invalid GUID string from %s%s", IMAP_FRAME_BAYES, suffix);
5657 }
5658 auto map_account = xaccAccountLookup (&guid, gnc_account_get_book (imapInfo.source_account));
5659 auto imap_node = static_cast <GncImapInfo*> (g_malloc (sizeof (GncImapInfo)));
5660 auto count = value->get <int64_t> ();
5661 imap_node->source_account = imapInfo.source_account;
5662 imap_node->map_account = map_account;
5663 imap_node->head = g_strdup_printf ("%s%s", IMAP_FRAME_BAYES, suffix);
5664 imap_node->match_string = g_strndup (&suffix[1], guid_start - 2);
5665 imap_node->category = g_strdup(" ");
5666 imap_node->count = g_strdup_printf ("%" G_GINT64_FORMAT, count);
5667 imapInfo.list = g_list_prepend (imapInfo.list, imap_node);
5668}
5669
5671{
5672 g_free (imapInfo->head);
5673 g_free (imapInfo->category);
5674 g_free (imapInfo->match_string);
5675 g_free (imapInfo->count);
5676 g_free (imapInfo);
5677}
5678
5679GList *
5681{
5682 check_import_map_data (gnc_account_get_book (acc));
5683 /* A dummy object which is used to hold the specified account, and the list
5684 * of data about which we care. */
5685 GncImapInfo imapInfo {acc, nullptr};
5686 qof_instance_foreach_slot_prefix (QOF_INSTANCE (acc), IMAP_FRAME_BAYES, &build_bayes, imapInfo);
5687 return g_list_reverse(imapInfo.list);
5688}
5689
5690GList *
5691gnc_account_imap_get_info (Account *acc, const char *category)
5692{
5693 GList *list = nullptr;
5694
5695 GncImapInfo imapInfo;
5696
5697 std::vector<std::string> path {IMAP_FRAME};
5698 if (category)
5699 path.emplace_back (category);
5700
5701 imapInfo.source_account = acc;
5702 imapInfo.list = list;
5703
5704 imapInfo.head = g_strdup (IMAP_FRAME);
5705 imapInfo.category = g_strdup (category);
5706
5707 if (qof_instance_has_path_slot (QOF_INSTANCE (acc), path))
5708 {
5709 qof_instance_foreach_slot (QOF_INSTANCE(acc), IMAP_FRAME, category,
5710 build_non_bayes, &imapInfo);
5711 }
5712 g_free (imapInfo.head);
5713 g_free (imapInfo.category);
5714 return g_list_reverse(imapInfo.list);
5715}
5716
5717/*******************************************************************************/
5718
5719gchar *
5720gnc_account_get_map_entry (Account *acc, const char *head, const char *category)
5721{
5722 return g_strdup (category ?
5723 get_kvp_string_path (acc, {head, category}) :
5724 get_kvp_string_path (acc, {head}));
5725}
5726
5727GncGUID *
5728gnc_account_get_map_guid_entry (Account *acc, const char *head, const char *category)
5729{
5730 return category ?
5731 get_kvp_guid_path (acc, {head, category}) :
5732 get_kvp_guid_path (acc, {head});
5733}
5734
5735void
5736gnc_account_delete_map_entry (Account *acc, char *head, char *category,
5737 char *match_string, gboolean empty)
5738{
5739 if (acc != nullptr)
5740 {
5741 std::vector<std::string> path {head};
5742 if (category)
5743 path.emplace_back (category);
5744 if (match_string)
5745 path.emplace_back (match_string);
5746
5747 if (qof_instance_has_path_slot (QOF_INSTANCE (acc), path))
5748 {
5750 if (empty)
5751 qof_instance_slot_path_delete_if_empty (QOF_INSTANCE(acc), path);
5752 else
5753 qof_instance_slot_path_delete (QOF_INSTANCE(acc), path);
5754 PINFO("Account is '%s', head is '%s', category is '%s', match_string is'%s'",
5755 xaccAccountGetName (acc), head, category, match_string);
5756 qof_instance_set_dirty (QOF_INSTANCE(acc));
5758 }
5759 }
5760}
5761
5762void
5764{
5765 if (acc != nullptr)
5766 {
5767 auto slots = qof_instance_get_slots_prefix (QOF_INSTANCE (acc), IMAP_FRAME_BAYES);
5768 if (!slots.size()) return;
5770 for (auto const & entry : slots)
5771 {
5772 qof_instance_slot_path_delete (QOF_INSTANCE (acc), {entry.first});
5773 }
5774 qof_instance_set_dirty (QOF_INSTANCE(acc));
5776 }
5777}
5778
5779/* ================================================================ */
5780/* QofObject function implementation and registration */
5781
5782static void
5783destroy_all_child_accounts (Account *acc, gpointer data)
5784{
5786 xaccAccountDestroy (acc);
5787}
5788
5789static void
5790gnc_account_book_end(QofBook* book)
5791{
5792 Account *root_account = gnc_book_get_root_account (book);
5793 GList *accounts;
5794
5795 if (!root_account)
5796 return;
5797
5798 accounts = gnc_account_get_descendants (root_account);
5799
5800 if (accounts)
5801 {
5802 accounts = g_list_reverse (accounts);
5803 g_list_foreach (accounts, (GFunc)destroy_all_child_accounts, nullptr);
5804 g_list_free (accounts);
5805 }
5806 xaccAccountBeginEdit (root_account);
5807 xaccAccountDestroy (root_account);
5808}
5809
5810#ifdef _MSC_VER
5811/* MSVC compiler doesn't have C99 "designated initializers"
5812 * so we wrap them in a macro that is empty on MSVC. */
5813# define DI(x) /* */
5814#else
5815# define DI(x) x
5816#endif
5817static QofObject account_object_def =
5818{
5819 DI(.interface_version = ) QOF_OBJECT_VERSION,
5820 DI(.e_type = ) GNC_ID_ACCOUNT,
5821 DI(.type_label = ) "Account",
5822 DI(.create = ) (void*(*)(QofBook*)) xaccMallocAccount,
5823 DI(.book_begin = ) nullptr,
5824 DI(.book_end = ) gnc_account_book_end,
5825 DI(.is_dirty = ) qof_collection_is_dirty,
5826 DI(.mark_clean = ) qof_collection_mark_clean,
5827 DI(.foreach = ) qof_collection_foreach,
5828 DI(.printable = ) (const char * (*)(gpointer)) xaccAccountGetName,
5829 DI(.version_cmp = ) (int (*)(gpointer, gpointer)) qof_instance_version_cmp,
5830};
5831
5832gboolean xaccAccountRegister (void)
5833{
5834 static QofParam params[] =
5835 {
5836 {
5837 ACCOUNT_NAME_, QOF_TYPE_STRING,
5840 },
5841 {
5842 ACCOUNT_CODE_, QOF_TYPE_STRING,
5845 },
5846 {
5847 ACCOUNT_DESCRIPTION_, QOF_TYPE_STRING,
5850 },
5851 {
5852 ACCOUNT_COLOR_, QOF_TYPE_STRING,
5855 },
5856 {
5857 ACCOUNT_FILTER_, QOF_TYPE_STRING,
5860 },
5861 {
5862 ACCOUNT_SORT_ORDER_, QOF_TYPE_STRING,
5865 },
5866 {
5867 ACCOUNT_SORT_REVERSED_, QOF_TYPE_BOOLEAN,
5870 },
5871 {
5872 ACCOUNT_NOTES_, QOF_TYPE_STRING,
5875 },
5876 {
5877 ACCOUNT_PRESENT_, QOF_TYPE_NUMERIC,
5878 (QofAccessFunc) xaccAccountGetPresentBalance, nullptr
5879 },
5880 {
5881 ACCOUNT_BALANCE_, QOF_TYPE_NUMERIC,
5883 },
5884 {
5885 ACCOUNT_CLEARED_, QOF_TYPE_NUMERIC,
5887 },
5888 {
5889 ACCOUNT_RECONCILED_, QOF_TYPE_NUMERIC,
5891 },
5892 {
5893 ACCOUNT_TYPE_, QOF_TYPE_STRING,
5894 (QofAccessFunc) qofAccountGetTypeString,
5895 (QofSetterFunc) qofAccountSetType
5896 },
5897 {
5898 ACCOUNT_FUTURE_MINIMUM_, QOF_TYPE_NUMERIC,
5899 (QofAccessFunc) xaccAccountGetProjectedMinimumBalance, nullptr
5900 },
5901 {
5902 ACCOUNT_TAX_RELATED, QOF_TYPE_BOOLEAN,
5905 },
5906 {
5907 ACCOUNT_OPENING_BALANCE_, QOF_TYPE_BOOLEAN,
5910 },
5911 {
5912 ACCOUNT_SCU, QOF_TYPE_INT32,
5915 },
5916 {
5917 ACCOUNT_NSCU, QOF_TYPE_BOOLEAN,
5920 },
5921 {
5922 ACCOUNT_PARENT, GNC_ID_ACCOUNT,
5924 (QofSetterFunc) qofAccountSetParent
5925 },
5926 {
5927 QOF_PARAM_BOOK, QOF_ID_BOOK,
5929 },
5930 {
5931 QOF_PARAM_GUID, QOF_TYPE_GUID,
5933 },
5934 { nullptr },
5935 };
5936
5937 qof_class_register (GNC_ID_ACCOUNT, (QofSortFunc) qof_xaccAccountOrder, params);
5938
5939 return qof_object_register (&account_object_def);
5940}
5941
5942/* ======================= UNIT TESTING ACCESS =======================
5943 * The following functions are for unit testing use only.
5944 */
5945static AccountPrivate*
5946utest_account_get_private (Account *acc)
5947{
5948 return GET_PRIVATE (acc);
5949}
5950
5952_utest_account_fill_functions(void)
5953{
5955
5956 func->get_private = utest_account_get_private;
5957 func->coll_get_root_account = gnc_coll_get_root_account;
5958 func->xaccFreeAccountChildren = xaccFreeAccountChildren;
5959 func->xaccFreeAccount = xaccFreeAccount;
5960 func->qofAccountSetParent = qofAccountSetParent;
5961 func->gnc_account_lookup_by_full_name_helper =
5962 gnc_account_lookup_by_full_name_helper;
5963
5964 return func;
5965}
5966/* ======================= END OF FILE =========================== */
This is the private header for the account structure.
Account public routines (C++ api)
API for Transactions and Splits (journal entries)
API for Transactions and Splits (journal entries)
Additional event handling code.
Utility functions for file access.
GLib helper routines.
Generic api to store and retrieve preferences.
a simple price database for gnucash
const char * xaccAccountGetName(const Account *acc)
Get the account's name.
Definition Account.cpp:3277
void xaccAccountSetOnlineID(Account *acc, const char *id)
Set the account's online_id, the identifier (e.g.
Definition Account.cpp:2649
void xaccAccountRecomputeBalance(Account *acc)
The following recompute the partial balances (stored with the transaction) and the total balance,...
Definition Account.cpp:2263
const char * xaccAccountGetDescription(const Account *acc)
Get the account's description.
Definition Account.cpp:3331
void xaccAccountSetColor(Account *acc, const char *str)
Set the account's Color.
Definition Account.cpp:2601
gint gnc_account_get_current_depth(const Account *account)
Return the number of levels of this account below the root account.
Definition Account.cpp:2998
const char * xaccAccountGetColor(const Account *acc)
Get the account's color.
Definition Account.cpp:3338
gboolean xaccAccountHasAncestor(const Account *acc, const Account *ancestor)
Returns true if the account is 'ancestor' or has 'ancestor' as an ancestor.
Definition Account.cpp:4212
GList * gnc_account_list_name_violations(QofBook *book, const gchar *separator)
Runs through all the accounts and returns a list of account names that contain the provided separator...
Definition Account.cpp:273
void xaccAccountCommitEdit(Account *acc)
ThexaccAccountCommitEdit() subroutine is the second phase of a two-phase-commit wrapper for account u...
Definition Account.cpp:1504
void gnc_account_set_policy(Account *acc, GNCPolicy *policy)
Set the account's lot order policy.
Definition Account.cpp:2096
gboolean gnc_account_insert_split(Account *acc, Split *s)
Insert the given split from an account.
Definition Account.cpp:1931
gnc_numeric xaccAccountGetReconciledBalanceAsOfDate(Account *acc, time64 date)
Get the reconciled balance of the account at the end of the day of the date specified.
Definition Account.cpp:3533
gboolean gnc_account_get_defer_bal_computation(Account *acc)
Get the account's flag for deferred balance computation.
Definition Account.cpp:1912
Account * gnc_account_get_root(Account *acc)
This routine returns the root account of the account tree that the specified account belongs to.
Definition Account.cpp:2930
GList * gnc_account_get_descendants_sorted(const Account *account)
This function returns a GList containing all the descendants of the specified account,...
Definition Account.cpp:3040
gint gnc_account_child_index(const Account *parent, const Account *child)
Return the index of the specified child within the list of the parent's children.
Definition Account.cpp:2971
void xaccAccountDestroyAllTransactions(Account *acc)
Destroy all of the transactions that parent splits in an account.
Definition Account.cpp:1588
gchar * gnc_account_name_violations_errmsg(const gchar *separator, GList *invalid_account_names)
Composes a translatable error message showing which account names clash with the current account sepa...
Definition Account.cpp:235
void xaccAccountBeginEdit(Account *acc)
The xaccAccountBeginEdit() subroutine is the first phase of a two-phase-commit wrapper for account up...
Definition Account.cpp:1463
GNCAccountType
The account types are used to determine how the transaction data in the account is displayed.
Definition Account.h:103
gchar * gnc_account_get_full_name(const Account *account)
The gnc_account_get_full_name routine returns the fully qualified name of the account using the given...
Definition Account.cpp:3293
gnc_commodity * gnc_account_get_currency_or_parent(const Account *account)
Returns a gnc_commodity that is a currency, suitable for being a Transaction's currency.
Definition Account.cpp:3403
void gnc_account_append_child(Account *new_parent, Account *child)
This function will remove from the child account any pre-existing parent relationship,...
Definition Account.cpp:2824
GNCAccountType xaccAccountGetType(const Account *acc)
Returns the account's account type.
Definition Account.cpp:3255
const gchar * gnc_get_account_separator_string(void)
Returns the account separation character chosen by the user.
Definition Account.cpp:205
Account * gnc_account_nth_child(const Account *parent, gint num)
Return the n'th child account of the specified parent account.
Definition Account.cpp:2981
int xaccAccountGetCommoditySCUi(const Account *acc)
Return the 'internal' SCU setting.
Definition Account.cpp:2726
int xaccAccountGetCommoditySCU(const Account *acc)
Return the SCU for the account.
Definition Account.cpp:2733
void xaccAccountSetName(Account *acc, const char *str)
Set the account's name.
Definition Account.cpp:2446
GList * gnc_account_get_descendants(const Account *account)
This routine returns a flat list of all of the accounts that are descendants of the specified account...
Definition Account.cpp:3032
void gnc_account_join_children(Account *to_parent, Account *from_parent)
The gnc_account_join_children() subroutine will move (reparent) all child accounts from the from_pare...
Definition Account.cpp:4919
void xaccAccountSetCommoditySCU(Account *acc, int scu)
Set the SCU for the account.
Definition Account.cpp:2710
void xaccAccountSetFilter(Account *acc, const char *str)
Set the account's Filter.
Definition Account.cpp:2607
void dxaccAccountSetPriceSrc(Account *acc, const char *src)
Set a string that identifies the Finance::Quote backend that should be used to retrieve online prices...
Definition Account.cpp:4823
gboolean gnc_account_is_root(const Account *account)
This routine indicates whether the specified account is the root node of an account tree.
Definition Account.cpp:2941
Account * xaccAccountGainsAccount(Account *acc, gnc_commodity *curr)
Retrieve the gains account used by this account for the indicated currency, creating and recording a ...
Definition Account.cpp:4805
gnc_numeric xaccAccountGetBalanceAsOfDate(Account *acc, time64 date)
Get the balance of the account at the end of the day before the date specified.
Definition Account.cpp:3521
void gnc_account_set_balance_dirty(Account *acc)
Tell the account that the running balances may be incorrect and need to be recomputed.
Definition Account.cpp:1886
void xaccAccountSetNonStdSCU(Account *acc, gboolean flag)
Set the flag indicating that this account uses a non-standard SCU.
Definition Account.cpp:2746
gnc_numeric xaccAccountGetBalanceAsOfDateInCurrency(Account *acc, time64 date, gnc_commodity *report_commodity, gboolean include_children)
This function gets the balance at the end of the given date in the desired commodity.
Definition Account.cpp:3839
GList * gnc_account_get_children(const Account *account)
This routine returns a GList of all children accounts of the specified account.
Definition Account.cpp:2948
const char * xaccAccountGetSortOrder(const Account *acc)
Get the account's Sort Order.
Definition Account.cpp:3350
gint gnc_account_get_tree_depth(const Account *account)
Return the number of levels of descendants accounts below the specified account.
Definition Account.cpp:3017
int xaccAccountOrder(const Account *aa, const Account *ab)
The xaccAccountOrder() subroutine defines a sorting order on accounts.
Definition Account.cpp:2363
Account * gnc_account_create_root(QofBook *book)
Create a new root level account.
Definition Account.cpp:1272
#define xaccAccountGetGUID(X)
Definition Account.h:252
gboolean xaccAccountIsPriced(const Account *acc)
Returns true if the account is a stock, mutual fund or currency, otherwise false.
Definition Account.cpp:4539
void xaccAccountSetDescription(Account *acc, const char *str)
Set the account's description.
Definition Account.cpp:2485
void xaccAccountSortSplits(Account *acc, gboolean force)
The xaccAccountSortSplits() routine will resort the account's splits if the sort is dirty.
Definition Account.cpp:1992
gboolean gnc_account_remove_split(Account *acc, Split *s)
Remove the given split from an account.
Definition Account.cpp:1961
void xaccAccountDestroy(Account *acc)
The xaccAccountDestroy() routine can be used to get rid of an account.
Definition Account.cpp:1578
gboolean xaccAccountGetReconcileChildrenStatus(const Account *acc)
DOCUMENT ME!
Definition Account.cpp:4883
void gnc_account_merge_children(Account *parent)
The gnc_account_merge_children() subroutine will go through an account, merging all child accounts th...
Definition Account.cpp:4941
void xaccAccountSetSortReversed(Account *acc, gboolean sortreversed)
Set the account's Sort Order direction.
Definition Account.cpp:2619
void gnc_account_set_sort_dirty(Account *acc)
Tell the account believes that the splits may be incorrectly sorted and need to be resorted.
Definition Account.cpp:1872
void gnc_account_set_defer_bal_computation(Account *acc, gboolean defer)
Set the defer balance flag.
Definition Account.cpp:1899
void xaccAccountSetLastNum(Account *acc, const char *num)
Set the last num field of an Account.
Definition Account.cpp:4680
void gnc_account_set_start_reconciled_balance(Account *acc, const gnc_numeric start_baln)
This function will set the starting reconciled commodity balance for this account.
Definition Account.cpp:3442
Account * xaccCloneAccount(const Account *from, QofBook *book)
The xaccCloneAccount() routine makes a simple copy of the indicated account, placing it in the indica...
Definition Account.cpp:1289
gnc_numeric xaccAccountGetBalance(const Account *acc)
Get the current balance of the account, which may include future splits.
Definition Account.cpp:3455
const char * xaccAccountGetLastNum(const Account *acc)
Get the last num field of an Account.
Definition Account.cpp:4671
Split * gnc_account_find_split(const Account *acc, std::function< bool(const Split *)> predicate, bool reverse)
scans account split list (in forward or reverse order) until predicate split->bool returns true.
Definition Account.cpp:1155
Account * gnc_account_get_parent(const Account *acc)
This routine returns a pointer to the parent of the specified account.
Definition Account.cpp:2923
void xaccAccountSetNotes(Account *acc, const char *str)
Set the account's notes.
Definition Account.cpp:2643
void gnc_account_remove_child(Account *parent, Account *child)
This function will remove the specified child account from the specified parent account.
Definition Account.cpp:2886
void xaccAccountSetReconcileChildrenStatus(Account *acc, gboolean status)
DOCUMENT ME!
Definition Account.cpp:4870
gpointer gnc_account_foreach_descendant_until(const Account *acc, AccountCb2 thunk, gpointer user_data)
This method will traverse all children of this accounts and their descendants, calling 'func' on each...
Definition Account.cpp:3232
void xaccAccountSetType(Account *acc, GNCAccountType tip)
Set the account's type.
Definition Account.cpp:2425
const char * dxaccAccountGetPriceSrc(const Account *acc)
Get a string that identifies the Finance::Quote backend that should be used to retrieve online prices...
Definition Account.cpp:4835
gboolean xaccAccountEqual(const Account *aa, const Account *ab, gboolean check_guids)
Compare two accounts for equality - this is a deep compare.
Definition Account.cpp:1654
gboolean xaccAccountHasStockSplit(const Account *acc)
Returns true if the account has a stock split, otherwise false.
Definition Account.cpp:3495
GNCPolicy * gnc_account_get_policy(Account *acc)
Get the account's lot order policy.
Definition Account.cpp:2088
Account * xaccMallocAccount(QofBook *book)
Constructor.
Definition Account.cpp:1258
void gnc_account_set_start_cleared_balance(Account *acc, const gnc_numeric start_baln)
This function will set the starting cleared commodity balance for this account.
Definition Account.cpp:3429
void xaccAccountSetAssociatedAccount(Account *acc, const char *tag, const Account *assoc_acct)
Set the account's associated account e.g.
Definition Account.cpp:2657
const char * xaccAccountGetFilter(const Account *acc)
Get the account's filter.
Definition Account.cpp:3344
void xaccAccountSetCode(Account *acc, const char *str)
Set the account's accounting code.
Definition Account.cpp:2466
void gnc_account_foreach_child(const Account *acc, AccountCb thunk, gpointer user_data)
This method will traverse the immediate children of this accounts, calling 'func' on each account.
Definition Account.cpp:3213
const char * xaccAccountGetOnlineID(const Account *acc)
Get the account's online_id (see xaccAccountSetOnlineID).
Definition Account.cpp:3368
const char * xaccAccountGetNotes(const Account *acc)
Get the account's notes.
Definition Account.cpp:3362
void xaccAccountSetSortOrder(Account *acc, const char *str)
Set the account's Sort Order.
Definition Account.cpp:2613
Account * xaccAccountGetAssociatedAccount(const Account *acc, const char *tag)
Get the account's associated account e.g.
Definition Account.cpp:3375
void gnc_account_set_start_balance(Account *acc, const gnc_numeric start_baln)
This function will set the starting commodity balance for this account.
Definition Account.cpp:3417
gnc_numeric xaccAccountGetClearedBalance(const Account *acc)
Get the current balance of the account, only including cleared transactions.
Definition Account.cpp:3462
gnc_numeric xaccAccountGetReconciledBalance(const Account *acc)
Get the current balance of the account, only including reconciled transactions.
Definition Account.cpp:3469
gboolean xaccAccountGetNonStdSCU(const Account *acc)
Return boolean, indicating whether this account uses a non-standard SCU.
Definition Account.cpp:2762
GList * gnc_account_get_children_sorted(const Account *account)
This routine returns a GList of all children accounts of the specified account, ordered by xaccAccoun...
Definition Account.cpp:2957
gint gnc_account_n_descendants(const Account *account)
Return the number of descendants of the specified account.
Definition Account.cpp:2990
gint gnc_account_n_children(const Account *account)
Return the number of children of the specified account.
Definition Account.cpp:2964
gnc_numeric xaccAccountGetNoclosingBalanceAsOfDateInCurrency(Account *acc, time64 date, gnc_commodity *report_commodity, gboolean include_children)
This function gets the balance at the end of the given date, ignoring closing entries,...
Definition Account.cpp:3849
const char * xaccAccountGetCode(const Account *acc)
Get the account's accounting code.
Definition Account.cpp:3324
gboolean xaccAccountGetSortReversed(const Account *acc)
Get the account's Sort Order direction.
Definition Account.cpp:3356
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account's commodity
Definition Account.cpp:3396
void xaccAccountSetCommodity(Account *acc, gnc_commodity *com)
Set the account's commodity.
Definition Account.cpp:2666
Account * xaccAccountLookup(const GncGUID *guid, QofBook *book)
The xaccAccountLookup() subroutine will return the account associated with the given id,...
Definition Account.cpp:2038
@ ACCT_TYPE_INCOME
Income accounts are used to denote income.
Definition Account.h:140
@ ACCT_TYPE_MUTUAL
Mutual Fund accounts will typically be shown in registers which show three columns: price,...
Definition Account.h:125
@ ACCT_TYPE_ROOT
The hidden root account of an account tree.
Definition Account.h:153
@ ACCT_TYPE_CREDITLINE
line of credit – don't use this for now, see NUM_ACCOUNT_TYPES
Definition Account.h:171
@ ACCT_TYPE_TRADING
Account used to record multiple commodity transactions.
Definition Account.h:155
@ ACCT_TYPE_EXPENSE
Expense accounts are used to denote expenses.
Definition Account.h:143
@ ACCT_TYPE_BANK
The bank account type denotes a savings or checking account held at a bank.
Definition Account.h:107
@ ACCT_TYPE_PAYABLE
A/P account type.
Definition Account.h:151
@ ACCT_TYPE_ASSET
asset (and liability) accounts indicate generic, generalized accounts that are none of the above.
Definition Account.h:116
@ ACCT_TYPE_CURRENCY
The currency account type indicates that the account is a currency trading account.
Definition Account.h:129
@ ACCT_TYPE_MONEYMRKT
bank account type – don't use this for now, see NUM_ACCOUNT_TYPES
Definition Account.h:169
@ ACCT_TYPE_RECEIVABLE
A/R account type.
Definition Account.h:149
@ ACCT_TYPE_CASH
The cash account type is used to denote a shoe-box or pillowcase stuffed with * cash.
Definition Account.h:110
@ ACCT_TYPE_LIABILITY
liability (and asset) accounts indicate generic, generalized accounts that are none of the above.
Definition Account.h:119
@ ACCT_TYPE_CHECKING
bank account type – don't use this for now, see NUM_ACCOUNT_TYPES
Definition Account.h:165
@ ACCT_TYPE_EQUITY
Equity account is used to balance the balance sheet.
Definition Account.h:146
@ ACCT_TYPE_CREDIT
The Credit card account is used to denote credit (e.g.
Definition Account.h:113
@ NUM_ACCOUNT_TYPES
stop here; the following types just aren't ready for prime time
Definition Account.h:161
@ ACCT_TYPE_STOCK
Stock accounts will typically be shown in registers which show three columns: price,...
Definition Account.h:122
@ ACCT_TYPE_SAVINGS
bank account type – don't use this for now, see NUM_ACCOUNT_TYPES
Definition Account.h:167
@ ACCT_TYPE_INVALID
Not a type.
Definition Account.h:104
@ ACCT_TYPE_NONE
Not a type.
Definition Account.h:105
QofBackendError
The errors that can be reported to the GUI & other front-end users.
Definition qofbackend.h:58
QofCollection * qof_book_get_collection(const QofBook *book, QofIdType entity_type)
Return The table of entities of the given type.
Definition qofbook.cpp:521
gboolean qof_book_shutting_down(const QofBook *book)
Is the book shutting down?
Definition qofbook.cpp:447
gpointer(* QofAccessFunc)(gpointer object, const QofParam *param)
The QofAccessFunc defines an arbitrary function pointer for access functions.
Definition qofclass.h:124
void(* QofSetterFunc)(gpointer, gpointer)
The QofSetterFunc defines an function pointer for parameter setters.
Definition qofclass.h:131
void qof_class_register(QofIdTypeConst obj_name, QofSortFunc default_sort_function, const QofParam *params)
This function registers a new object class with the Qof subsystem.
Definition qofclass.cpp:86
int(* QofSortFunc)(gconstpointer, gconstpointer)
This function is the default sort function for a particular object type.
Definition qofclass.h:169
gboolean gnc_commodity_equiv(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equivalent.
gboolean gnc_commodity_equal(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equal.
#define GNC_COMMODITY_MAX_FRACTION
Max fraction is 10^9 because 10^10 would require changing it to an int64_t.
int gnc_commodity_get_fraction(const gnc_commodity *cm)
Retrieve the fraction for the specified commodity.
gnc_commodity * gnc_commodity_obtain_twin(const gnc_commodity *from, QofBook *book)
Given the commodity 'findlike', this routine will find and return the equivalent commodity (commodity...
gnc_commodity_table * gnc_commodity_table_get_table(QofBook *book)
Returns the commodity table associated with a book.
void gnc_commodity_increment_usage_count(gnc_commodity *cm)
Increment a commodity's internal counter that tracks how many accounts are using that commodity.
gnc_commodity * gnc_commodity_table_insert(gnc_commodity_table *table, gnc_commodity *comm)
Add a new commodity to the commodity table.
void gnc_commodity_decrement_usage_count(gnc_commodity *cm)
Decrement a commodity's internal counter that tracks how many accounts are using that commodity.
const char * gnc_commodity_get_mnemonic(const gnc_commodity *cm)
Retrieve the mnemonic for the specified commodity.
const char * gnc_commodity_get_unique_name(const gnc_commodity *cm)
Retrieve the 'unique' name for the specified commodity.
gboolean gnc_commodity_is_currency(const gnc_commodity *cm)
Checks to see if the specified commodity is an ISO 4217 recognized currency or a legacy currency.
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition gnc-date.h:87
time64 gnc_time64_get_today_end(void)
The gnc_time64_get_today_end() routine returns a time64 value corresponding to the last second of tod...
LotList * xaccAccountGetLotList(const Account *acc)
The xaccAccountGetLotList() routine returns a list of all lots in this account.
Definition Account.cpp:3963
gboolean xaccAccountGetHidden(const Account *acc)
Get the "hidden" flag for an account.
Definition Account.cpp:4178
gboolean xaccTransGetIsClosingTxn(const Transaction *trans)
Returns whether this transaction is a "closing transaction".
const char * gnc_account_get_credit_string(GNCAccountType acct_type)
Get the credit string associated with this account type.
Definition Account.cpp:4091
void xaccAccountSetTaxUSCopyNumber(Account *acc, gint64 copy_number)
Saves copy_number in KVP if it is greater than 1; if copy_number is zero, deletes KVP.
Definition Account.cpp:4066
GList LotList
GList of GNCLots.
Definition gnc-engine.h:205
gboolean xaccAccountStringToType(const char *str, GNCAccountType *type)
Conversion routines for the account types to/from strings that are used in persistent storage,...
Definition Account.cpp:4272
gboolean xaccAccountGetPlaceholder(const Account *acc)
Get the "placeholder" flag for an account.
Definition Account.cpp:4107
GNCPlaceholderType xaccAccountGetDescendantPlaceholder(const Account *acc)
Returns PLACEHOLDER_NONE if account is NULL or neither account nor any descendant of account is a pla...
Definition Account.cpp:4150
const char * xaccAccountTypeEnumAsString(GNCAccountType type)
Conversion routines for the account types to/from strings that are used in persistent storage,...
Definition Account.cpp:4235
gboolean xaccAccountGetAutoInterest(const Account *acc)
Get the "auto interest" flag for an account.
Definition Account.cpp:4163
gboolean xaccAccountGetReconcilePostponeDate(const Account *acc, time64 *postpone_date)
DOCUMENT ME!
Definition Account.cpp:4612
gboolean xaccAccountTypesCompatible(GNCAccountType parent_type, GNCAccountType child_type)
Return TRUE if accounts of type parent_type can have accounts of type child_type as children.
Definition Account.cpp:4442
GNCAccountType xaccAccountTypeGetFundamental(GNCAccountType t)
Convenience function to return the fundamental type asset/liability/income/expense/equity given an ac...
Definition Account.cpp:4483
gboolean xaccAccountGetLowerBalanceLimit(const Account *acc, gnc_numeric *balance)
Get the lower balance limit for the account.
Definition Account.cpp:4714
const char * gnc_account_get_debit_string(GNCAccountType acct_type)
Get the debit string associated with this account type.
Definition Account.cpp:4079
gboolean xaccAccountGetIsOpeningBalance(const Account *acc)
Get the "opening-balance" flag for an account.
Definition Account.cpp:4131
void xaccAccountSetAppendText(Account *acc, gboolean val)
Set the "import-append-text" flag for an account.
Definition Account.cpp:4125
void xaccAccountSetReconcileLastInterval(Account *acc, int months, int days)
DOCUMENT ME!
Definition Account.cpp:4602
void xaccAccountSetIsOpeningBalance(Account *acc, gboolean val)
Set the "opening-balance" flag for an account.
Definition Account.cpp:4141
gboolean xaccAccountGetAppendText(const Account *acc)
Get the "import-append-text" flag for an account.
Definition Account.cpp:4119
void xaccAccountSetIncludeSubAccountBalances(Account *acc, gboolean inc_sub)
Set whether to include balances of sub accounts.
Definition Account.cpp:4751
void xaccClearMarkDown(Account *acc, short val)
The xaccClearMarkDown will clear the mark only in this and in sub-accounts.
Definition Account.cpp:2072
gchar * gnc_account_get_map_entry(Account *acc, const char *head, const char *category)
Returns the text string pointed to by head and category for the Account, free the returned text.
Definition Account.cpp:5720
void gnc_account_delete_all_bayes_maps(Account *acc)
Delete all bayes entries for Account.
Definition Account.cpp:5763
void xaccTransCommitEdit(Transaction *trans)
The xaccTransCommitEdit() method indicates that the changes to the transaction and its splits are com...
const char * xaccTransGetDescription(const Transaction *trans)
Gets the transaction Description.
void DxaccAccountSetCurrency(Account *acc, gnc_commodity *currency)
Definition Account.cpp:2773
guint32 xaccAccountTypesCompatibleWith(GNCAccountType type)
Return the bitmask of account types compatible with a given type.
Definition Account.cpp:4355
gboolean xaccAccountIsHidden(const Account *acc)
Should this account be "hidden".
Definition Account.cpp:4190
Account * gnc_account_lookup_by_opening_balance(Account *account, gnc_commodity *commodity)
Find the opening balance account for the currency.
Definition Account.cpp:3109
void xaccAccountSetReconcileLastDate(Account *acc, time64 last_date)
DOCUMENT ME!
Definition Account.cpp:4572
gint64 xaccAccountGetTaxUSCopyNumber(const Account *acc)
Returns copy_number stored in KVP; if KVP doesn't exist or copy_number is zero, returns 1.
Definition Account.cpp:4059
void xaccAccountSetTaxUSCode(Account *acc, const char *code)
DOCUMENT ME!
Definition Account.cpp:4041
Account * gnc_account_lookup_by_name(const Account *parent, const char *name)
The gnc_account_lookup_by_name() subroutine fetches the account by name from the descendants of the s...
Definition Account.cpp:3081
void xaccAccountSetHigherBalanceLimit(Account *acc, gnc_numeric balance)
Set the higher balance limit for the account.
Definition Account.cpp:4721
int gnc_account_tree_staged_transaction_traversal(const Account *acc, unsigned int stage, TransactionCallback thunk, void *cb_data)
gnc_account_tree_staged_transaction_traversal() calls thunk on each transaction in the group whose cu...
Definition Account.cpp:5077
GList * gnc_account_lookup_by_type_and_commodity(Account *root, const char *name, GNCAccountType acctype, gnc_commodity *commodity)
Find a direct child account matching name, GNCAccountType, and/or commodity.
Definition Account.cpp:3176
GList * gnc_account_imap_get_info(Account *acc, const char *category)
Returns a GList of structure imap_info of all Non Bayesian mappings for required Account.
Definition Account.cpp:5691
GNCAccountType xaccAccountStringToEnum(const char *str)
Conversion routines for the account types to/from strings that are used in persistent storage,...
Definition Account.cpp:4306
gboolean xaccAccountIsAPARType(GNCAccountType t)
Convenience function to check if the account is a valid business account type (meaning an Accounts Pa...
Definition Account.cpp:4515
GNCPlaceholderType
DOCUMENT ME!
Definition Account.h:1283
gboolean xaccAccountGetTaxRelated(const Account *acc)
DOCUMENT ME!
Definition Account.cpp:4023
gboolean xaccTransIsOpen(const Transaction *trans)
The xaccTransIsOpen() method returns TRUE if the transaction is open for editing.
void xaccAccountSetReconcilePostponeDate(Account *acc, time64 postpone_date)
DOCUMENT ME!
Definition Account.cpp:4627
void xaccAccountSetAutoInterest(Account *acc, gboolean val)
Set the "auto interest" flag for an account.
Definition Account.cpp:4169
void gnc_account_tree_begin_staged_transaction_traversals(Account *account)
gnc_account_tree_begin_staged_transaction_traversals() resets the traversal marker inside every trans...
Definition Account.cpp:5042
LotList * xaccAccountFindOpenLots(const Account *acc, gboolean(*match_func)(GNCLot *lot, gpointer user_data), gpointer user_data, GCompareFunc sort_func)
Find a list of open lots that match the match_func.
Definition Account.cpp:3970
gboolean xaccAccountIsAssetLiabType(GNCAccountType t)
Convenience function to check if the account is a valid Asset or Liability type, but not a business a...
Definition Account.cpp:4469
void xaccAccountInsertLot(Account *acc, GNCLot *lot)
The xaccAccountInsertLot() method will register the indicated lot with this account.
Definition Account.cpp:2128
gboolean xaccAccountGetReconcileLastInterval(const Account *acc, int *months, int *days)
DOCUMENT ME!
Definition Account.cpp:4581
void xaccAccountSetMark(Account *acc, short m)
Set a mark on the account.
Definition Account.cpp:2050
void dxaccAccountSetQuoteTZ(Account *acc, const char *tz)
Set the timezone to be used when interpreting the results from a given Finance::Quote backend.
Definition Account.cpp:4848
guint32 xaccAccountTypesValid(void)
Returns the bitmask of the account type enums that are valid.
Definition Account.cpp:4460
Split * xaccAccountFindSplitByDesc(const Account *acc, const char *description)
Returns a pointer to the split, not a copy.
Definition Account.cpp:4896
void gnc_account_imap_info_destroy(GncImapInfo *imapInfo)
Clean destructor for the imap_info structure of Bayesian mappings.
Definition Account.cpp:5670
time64 gnc_account_get_earliest_date(const Account *account)
Returns the date of the earliest split in the account, or INT64_MAX.
Definition Account.cpp:5115
gboolean xaccAccountGetHigherBalanceLimit(const Account *acc, gnc_numeric *balance)
Get the higher balance limit for the account.
Definition Account.cpp:4707
void gnc_account_delete_map_entry(Account *acc, char *head, char *category, char *match_string, gboolean empty)
Delete the entry for Account pointed to by head,category and match_string, if empty is TRUE then use ...
Definition Account.cpp:5736
const char * xaccAccountGetTaxUSCode(const Account *acc)
DOCUMENT ME!
Definition Account.cpp:4035
void xaccAccountSetReconcilePostponeBalance(Account *acc, gnc_numeric balance)
DOCUMENT ME!
Definition Account.cpp:4652
const char * xaccAccountGetTaxUSPayerNameSource(const Account *acc)
DOCUMENT ME!
Definition Account.cpp:4047
void gnc_account_reset_convert_bayes_to_flat(void)
Reset the flag that indicates the function imap_convert_bayes_to_flat has been run.
Definition Account.cpp:5481
gboolean xaccAccountGetReconcilePostponeBalance(const Account *acc, gnc_numeric *balance)
DOCUMENT ME!
Definition Account.cpp:4636
void xaccAccountMoveAllSplits(Account *accfrom, Account *accto)
The xaccAccountMoveAllSplits() routine reassigns each of the splits in accfrom to accto.
Definition Account.cpp:2184
void xaccAccountSetTaxRelated(Account *acc, gboolean tax_related)
DOCUMENT ME!
Definition Account.cpp:4029
gboolean xaccAccountGetReconcileLastDate(const Account *acc, time64 *last_date)
DOCUMENT ME!
Definition Account.cpp:4554
void xaccAccountClearLowerBalanceLimit(Account *acc)
Clear the lower balance limit for the account.
Definition Account.cpp:4739
void xaccAccountSetLowerBalanceLimit(Account *acc, gnc_numeric balance)
Set the lower balance limit for the account.
Definition Account.cpp:4727
Account * gnc_account_imap_find_account_bayes(Account *acc, GList *tokens)
Look up an Account in the map.
Definition Account.cpp:5513
void xaccAccountClearReconcilePostpone(Account *acc)
DOCUMENT ME!
Definition Account.cpp:4662
void xaccAccountClearHigherBalanceLimit(Account *acc)
Clear the higher balance limit for the account.
Definition Account.cpp:4733
void xaccAccountBeginStagedTransactionTraversals(const Account *account)
xaccAccountBeginStagedTransactionTraversals() resets the traversal marker for each transaction which ...
Definition Account.cpp:5019
void gnc_account_imap_add_account_bayes(Account *acc, GList *tokens, Account *added_acc)
Updates the imap for a given account using a list of tokens.
Definition Account.cpp:5559
guint32 xaccParentAccountTypesCompatibleWith(GNCAccountType type)
Return the bitmask of parent account types compatible with a given type.
Definition Account.cpp:4395
void xaccAccountSetHidden(Account *acc, gboolean val)
Set the "hidden" flag for an account.
Definition Account.cpp:4184
int xaccAccountStagedTransactionTraversal(const Account *acc, unsigned int stage, TransactionCallback thunk, void *cb_data)
xaccAccountStagedTransactionTraversal() calls thunk on each transaction in account a whose current ma...
Definition Account.cpp:5050
Transaction * xaccAccountFindTransByDesc(const Account *acc, const char *description)
Returns a pointer to the transaction, not a copy.
Definition Account.cpp:4909
GList * gnc_account_imap_get_info_bayes(Account *acc)
Returns a GList of structure imap_info of all Bayesian mappings for required Account.
Definition Account.cpp:5680
gboolean xaccTransactionTraverse(Transaction *trans, int stage)
xaccTransactionTraverse() checks the stage of the given transaction.
Definition Account.cpp:5027
gboolean xaccAccountGetIncludeSubAccountBalances(const Account *acc)
Get whether to include balances of sub accounts.
Definition Account.cpp:4745
void xaccAccountSetPlaceholder(Account *acc, gboolean val)
Set the "placeholder" flag for an account.
Definition Account.cpp:4113
gboolean xaccAccountIsEquityType(GNCAccountType t)
Convenience function to check if the account is a valid Equity type.
Definition Account.cpp:4527
Account * gnc_account_lookup_by_full_name(const Account *any_acc, const gchar *name)
The gnc_account_lookup_full_name() subroutine works like gnc_account_lookup_by_name,...
Definition Account.cpp:3151
GList SplitList
GList of Split.
Definition gnc-engine.h:207
void xaccTransBeginEdit(Transaction *trans)
The xaccTransBeginEdit() method must be called before any changes are made to a transaction or any of...
SplitList * xaccAccountGetSplitList(const Account *acc)
The xaccAccountGetSplitList() routine returns a pointer to a GList of the splits in the account.
Definition Account.cpp:3937
Account * gnc_account_lookup_by_code(const Account *parent, const char *code)
The gnc_account_lookup_by_code() subroutine works like gnc_account_lookup_by_name,...
Definition Account.cpp:3094
GncGUID * gnc_account_get_map_guid_entry(Account *acc, const char *head, const char *category)
Returns the guid pointed to by head and category for the Account, free the returned guid.
Definition Account.cpp:5728
void xaccClearMark(Account *acc, short val)
Get the mark set by xaccAccountSetMark short xaccAccountGetMark (const Account *account);.
Definition Account.cpp:2061
void xaccAccountSetTaxUSPayerNameSource(Account *acc, const char *source)
DOCUMENT ME!
Definition Account.cpp:4053
time64 xaccTransGetDate(const Transaction *trans)
Retrieve the posted date of the transaction.
gnc_commodity * DxaccAccountGetCurrency(const Account *acc)
Definition Account.cpp:3384
const char * xaccAccountGetTypeStr(GNCAccountType type)
The xaccAccountGetTypeStr() routine returns a string suitable for use in the GUI/Interface.
Definition Account.cpp:4345
const char * dxaccAccountGetQuoteTZ(const Account *acc)
Get the timezone to be used when interpreting the results from a given Finance::Quote backend.
Definition Account.cpp:4859
gint xaccAccountForEachTransaction(const Account *acc, TransactionCallback proc, void *data)
The xaccAccountForEachTransaction() routine will traverse all of the transactions in account and call...
Definition Account.cpp:5138
QofInstance * qof_collection_lookup_entity(const QofCollection *col, const GncGUID *guid)
Find the entity going only from its guid.
Definition qofid.cpp:209
gboolean qof_collection_is_dirty(const QofCollection *col)
Return value of 'dirty' flag on collection.
Definition qofid.cpp:232
gpointer qof_collection_get_data(const QofCollection *col)
Store and retrieve arbitrary object-defined data.
Definition qofid.cpp:266
void qof_event_resume(void)
Resume engine event generation.
Definition qofevent.cpp:156
void qof_event_gen(QofInstance *entity, QofEventId event_id, gpointer event_data)
Invoke all registered event handlers using the given arguments.
Definition qofevent.cpp:231
void qof_event_suspend(void)
Suspend all engine events.
Definition qofevent.cpp:145
#define GNC_EVENT_ITEM_ADDED
These events are used when a split is added to an account.
Definition gnc-event.h:45
gchar * guid_to_string(const GncGUID *guid)
The guid_to_string() routine returns a null-terminated string encoding of the id.
Definition guid.cpp:199
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
Definition guid.h:84
GncGUID * guid_copy(const GncGUID *guid)
Returns a newly allocated GncGUID that matches the passed-in GUID.
Definition guid.cpp:155
gchar * guid_to_string_buff(const GncGUID *guid, gchar *str)
The guid_to_string_buff() routine puts a null-terminated string encoding of the id into the memory po...
Definition guid.cpp:208
gchar * gnc_g_list_stringjoin(GList *list_of_strings, const gchar *sep)
Return a string joining a GList whose elements are gchar* strings.
int safe_utf8_collate(const char *da, const char *db)
Collate two UTF-8 strings.
QofBook * qof_instance_get_book(gconstpointer inst)
Return the book pointer.
const GncGUID * qof_instance_get_guid(gconstpointer inst)
Return the GncGUID of this instance.
int qof_instance_version_cmp(const QofInstance *left, const QofInstance *right)
Compare two instances, based on their last update times.
void qof_instance_init_data(QofInstance *inst, QofIdType type, QofBook *book)
Initialise the settings associated with an instance.
gboolean qof_instance_get_destroying(gconstpointer ptr)
Retrieve the flag that indicates whether or not this object is about to be destroyed.
gboolean qof_instance_books_equal(gconstpointer ptr1, gconstpointer ptr2)
See if two QofInstances share the same book.
gint qof_instance_guid_compare(gconstpointer ptr1, gconstpointer ptr2)
Compare the GncGUID values of two instances.
#define PINFO(format, args...)
Print an informational note.
Definition qoflog.h:256
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition qoflog.h:282
#define PERR(format, args...)
Log a serious error.
Definition qoflog.h:244
#define PWARN(format, args...)
Log a warning.
Definition qoflog.h:250
#define ENTER(format, args...)
Print a function entry debugging message.
Definition qoflog.h:272
Account * gnc_lot_get_account(const GNCLot *lot)
Returns the account with which this lot is associated.
Definition gnc-lot.cpp:377
gboolean gnc_lot_is_closed(GNCLot *lot)
Returns closed status of the given lot.
Definition gnc-lot.cpp:367
gnc_numeric gnc_numeric_sub(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Return a-b.
gchar * gnc_numeric_to_string(gnc_numeric n)
Convert to string.
#define GNC_DENOM_AUTO
Values that can be passed as the 'denom' argument.
gnc_numeric gnc_numeric_div(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Division.
gnc_numeric gnc_numeric_mul(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Multiply a times b, returning the product.
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
int gnc_numeric_compare(gnc_numeric a, gnc_numeric b)
Returns 1 if a>b, -1 if b>a, 0 if a == b
gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
Equivalence predicate: Returns TRUE (1) if a and b represent the same number.
gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Return a+b.
@ GNC_HOW_RND_ROUND_HALF_UP
Round to the nearest integer, rounding away from zero when there are two equidistant nearest integers...
@ GNC_HOW_DENOM_REDUCE
Reduce the result value by common factor elimination, using the smallest possible value for the denom...
void qof_collection_mark_clean(QofCollection *)
reset value of dirty flag
Definition qofid.cpp:238
void qof_collection_insert_entity(QofCollection *, QofInstance *)
Take entity, remove it from whatever collection its currently in, and place it in a new collection.
Definition qofid.cpp:95
#define QOF_OBJECT_VERSION
Defines the version of the core object object registration interface.
Definition qofobject.h:63
gboolean qof_object_register(const QofObject *object)
Register new types of object objects.
GNCPolicy * xaccGetFIFOPolicy(void)
First-in, First-out Policy This policy will create FIFO Lots.
Definition policy.cpp:155
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Get a boolean value from the preferences backend.
gnc_numeric gnc_pricedb_convert_balance_nearest_before_price_t64(GNCPriceDB *pdb, gnc_numeric balance, const gnc_commodity *balance_currency, const gnc_commodity *new_currency, time64 t)
Convert a balance from one currency to another using the price nearest to before the given time.
gnc_numeric gnc_pricedb_convert_balance_latest_price(GNCPriceDB *pdb, gnc_numeric balance, const gnc_commodity *balance_currency, const gnc_commodity *new_currency)
Convert a balance from one currency to another using the most recent price between the two.
GNCPriceDB * gnc_pricedb_get_db(QofBook *book)
Return the pricedb associated with the book.
#define QOF_PARAM_BOOK
"Known" Object Parameters – all objects must support these
Definition qofquery.h:108
Transaction * xaccSplitGetParent(const Split *split)
Returns the parent transaction of the split.
void xaccSplitSetAdjustedAmount(Split *s, gnc_numeric amt)
Sets the stock split adjusted amount of a split.
Definition Split.cpp:1357
gboolean xaccSplitDestroy(Split *split)
Destructor.
Definition Split.cpp:1517
gint xaccSplitOrder(const Split *sa, const Split *sb)
The xaccSplitOrder(sa,sb) method is useful for sorting.
Definition Split.cpp:1547
gnc_numeric xaccSplitGetNoclosingBalance(const Split *s)
The noclosing-balance is the currency-denominated balance of all transactions except 'closing' transa...
Definition Split.cpp:1333
void xaccSplitSetAmount(Split *split, gnc_numeric amt)
The xaccSplitSetAmount() method sets the amount in the account's commodity that the split should have...
gnc_numeric xaccSplitGetReconciledBalance(const Split *s)
Returns the reconciled-balance of this split.
Definition Split.cpp:1345
Account * xaccSplitGetAccount(const Split *split)
Returns the account of this split, which was set through xaccAccountInsertSplit().
#define NREC
not reconciled or cleared
Definition Split.h:76
#define FREC
frozen into accounting period
Definition Split.h:75
gnc_numeric xaccSplitGetAdjustedAmount(const Split *s)
Returns the stock-split adjusted amount of the split in the account's commodity.
Definition Split.cpp:1351
gnc_numeric xaccSplitGetBalance(const Split *s)
Returns the running balance up to and including the indicated split.
Definition Split.cpp:1327
#define YREC
The Split has been reconciled.
Definition Split.h:74
gboolean xaccSplitIsStockSplit(Split *s)
Returns true if the split is of type stock split.
Definition Split.cpp:2089
gnc_numeric xaccSplitGetAmount(const Split *split)
Returns the amount of the split in the account's commodity.
void gnc_features_set_used(QofBook *book, const gchar *feature)
Indicate that the current book uses the given feature.
gint null_strcmp(const gchar *da, const gchar *db)
The null_strcmp compares strings a and b the same way that strcmp() does, except that either may be n...
Definition qofutil.cpp:123
const char * qof_string_cache_insert(const char *key)
You can use this function with g_hash_table_insert(), for the key (or value), as long as you use the ...
const char * qof_string_cache_replace(char const *dst, char const *src)
Same as CACHE_REPLACE below, but safe to call from C++.
gboolean qof_commit_edit_part2(QofInstance *inst, void(*on_error)(QofInstance *, QofBackendError), void(*on_done)(QofInstance *), void(*on_free)(QofInstance *))
part2 – deal with the backend
gboolean qof_commit_edit(QofInstance *inst)
commit_edit helpers
gboolean qof_begin_edit(QofInstance *inst)
begin_edit
void qof_string_cache_remove(const char *key)
You can use this function as a destroy notifier for a GHashTable that uses common strings as keys (or...
QOF event handling interface.
holds an account guid and its corresponding integer probability the integer probability is some facto...
Definition Account.cpp:5261
intermediate values used to calculate the bayes probability of a given account where p(AB) = (a*b)/[a...
Definition Account.cpp:5237
STRUCTS.
The type used to store guids in C.
Definition guid.h:75
@ INT64
QOF_TYPE_INT64 gint64.
Definition kvp-value.hpp:58
@ STRING
QOF_TYPE_STRING gchar*.
Definition kvp-value.hpp:61
QofBook reference.
Definition qofbook-p.hpp:47
total_count and the token_count for a given account let us calculate the probability of a given accou...
Definition Account.cpp:5252