GnuCash c935c2f+
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 if (!GNC_IS_ACCOUNT (acc))
1144 return;
1145
1146 auto& splits{GET_PRIVATE(acc)->splits};
1147 std::for_each (splits.begin(), splits.end(), func);
1148}
1149
1150void
1151gnc_account_foreach_split_until_date (const Account *acc, time64 end_date,
1152 std::function<void(Split*)> f)
1153{
1154 if (!GNC_IS_ACCOUNT (acc))
1155 return;
1156
1157 auto after_date = [](time64 end_date, auto s) -> bool
1158 { return (xaccTransGetDate (xaccSplitGetParent (s)) > end_date); };
1159
1160 auto& splits{GET_PRIVATE(acc)->splits};
1161 auto after_date_iter = std::upper_bound (splits.begin(), splits.end(), end_date, after_date);
1162 std::for_each (splits.begin(), after_date_iter, f);
1163}
1164
1165
1166Split*
1167gnc_account_find_split (const Account *acc, std::function<bool(const Split*)> predicate,
1168 bool reverse)
1169{
1170 if (!GNC_IS_ACCOUNT (acc))
1171 return nullptr;
1172
1173 const auto& splits{GET_PRIVATE(acc)->splits};
1174 if (reverse)
1175 {
1176 auto latest = std::find_if(splits.rbegin(), splits.rend(), predicate);
1177 return (latest == splits.rend()) ? nullptr : *latest;
1178 }
1179 else
1180 {
1181 auto earliest = std::find_if(splits.begin(), splits.end(), predicate);
1182 return (earliest == splits.end()) ? nullptr : *earliest;
1183 }
1184}
1185
1186/********************************************************************\
1187\********************************************************************/
1188
1189QofBook *
1190gnc_account_get_book(const Account *account)
1191{
1192 if (!account) return nullptr;
1193 return qof_instance_get_book(QOF_INSTANCE(account));
1194}
1195
1196/********************************************************************\
1197\********************************************************************/
1198
1199static Account *
1200gnc_coll_get_root_account (QofCollection *col)
1201{
1202 if (!col) return nullptr;
1203 return static_cast<Account*>(qof_collection_get_data (col));
1204}
1205
1206static void
1207gnc_coll_set_root_account (QofCollection *col, Account *root)
1208{
1209 AccountPrivate *rpriv;
1210 Account *old_root;
1211 if (!col) return;
1212
1213 old_root = gnc_coll_get_root_account (col);
1214 if (old_root == root) return;
1215
1216 /* If the new root is already linked into the tree somewhere, then
1217 * remove it from its current position before adding it at the
1218 * top. */
1219 rpriv = GET_PRIVATE(root);
1220 if (rpriv->parent)
1221 {
1223 gnc_account_remove_child(rpriv->parent, root);
1225 }
1226
1227 qof_collection_set_data (col, root);
1228
1229 if (old_root)
1230 {
1231 xaccAccountBeginEdit (old_root);
1232 xaccAccountDestroy (old_root);
1233 }
1234}
1235
1236Account *
1237gnc_book_get_root_account (QofBook *book)
1238{
1239 QofCollection *col;
1240 Account *root;
1241
1242 if (!book) return nullptr;
1243 col = qof_book_get_collection (book, GNC_ID_ROOT_ACCOUNT);
1244 root = gnc_coll_get_root_account (col);
1245 if (root == nullptr && !qof_book_shutting_down(book))
1246 root = gnc_account_create_root(book);
1247 return root;
1248}
1249
1250void
1251gnc_book_set_root_account (QofBook *book, Account *root)
1252{
1253 QofCollection *col;
1254 if (!book) return;
1255
1256 if (root && gnc_account_get_book(root) != book)
1257 {
1258 PERR ("cannot mix and match books freely!");
1259 return;
1260 }
1261
1262 col = qof_book_get_collection (book, GNC_ID_ROOT_ACCOUNT);
1263 gnc_coll_set_root_account (col, root);
1264}
1265
1266/********************************************************************\
1267\********************************************************************/
1268
1269Account *
1271{
1272 Account *acc;
1273
1274 g_return_val_if_fail (book, nullptr);
1275
1276 acc = static_cast<Account*>(g_object_new (GNC_TYPE_ACCOUNT, nullptr));
1277 xaccInitAccount (acc, book);
1278 qof_event_gen (&acc->inst, QOF_EVENT_CREATE, nullptr);
1279
1280 return acc;
1281}
1282
1283Account *
1285{
1286 Account *root;
1287 AccountPrivate *rpriv;
1288
1289 root = xaccMallocAccount(book);
1290 rpriv = GET_PRIVATE(root);
1292 rpriv->type = ACCT_TYPE_ROOT;
1293 rpriv->accountName = qof_string_cache_replace(rpriv->accountName, "Root Account");
1294 mark_account (root);
1296 gnc_book_set_root_account(book, root);
1297 return root;
1298}
1299
1300Account *
1302{
1303 Account *ret;
1304 AccountPrivate *from_priv, *priv;
1305
1306 g_return_val_if_fail(GNC_IS_ACCOUNT(from), nullptr);
1307 g_return_val_if_fail(QOF_IS_BOOK(book), nullptr);
1308
1309 ENTER (" ");
1310 ret = static_cast<Account*>(g_object_new (GNC_TYPE_ACCOUNT, nullptr));
1311 g_return_val_if_fail (ret, nullptr);
1312
1313 from_priv = GET_PRIVATE(from);
1314 priv = GET_PRIVATE(ret);
1315 xaccInitAccount (ret, book);
1316
1317 /* Do not Begin/CommitEdit() here; give the caller
1318 * a chance to fix things up, and let them do it.
1319 * Also let caller issue the generate_event (EVENT_CREATE) */
1320 priv->type = from_priv->type;
1321
1322 priv->accountName = qof_string_cache_replace(priv->accountName, from_priv->accountName);
1323 priv->accountCode = qof_string_cache_replace(priv->accountCode, from_priv->accountCode);
1324 priv->description = qof_string_cache_replace(priv->description, from_priv->description);
1325
1326 qof_instance_copy_kvp (QOF_INSTANCE (ret), QOF_INSTANCE (from));
1327
1328 /* The new book should contain a commodity that matches
1329 * the one in the old book. Find it, use it. */
1330 priv->commodity = gnc_commodity_obtain_twin(from_priv->commodity, book);
1331 gnc_commodity_increment_usage_count(priv->commodity);
1332
1333 priv->commodity_scu = from_priv->commodity_scu;
1334 priv->non_standard_scu = from_priv->non_standard_scu;
1335
1336 qof_instance_set_dirty(&ret->inst);
1337 LEAVE (" ");
1338 return ret;
1339}
1340
1341/********************************************************************\
1342\********************************************************************/
1343
1344static void
1345xaccFreeOneChildAccount (Account *acc)
1346{
1347 /* FIXME: this code is kind of hacky. actually, all this code
1348 * seems to assume that the account edit levels are all 1. */
1349 if (qof_instance_get_editlevel(acc) == 0)
1351 xaccAccountDestroy(acc);
1352}
1353
1354static void
1355xaccFreeAccountChildren (Account *acc)
1356{
1357 auto priv{GET_PRIVATE(acc)};
1358 /* Copy the list since it will be modified */
1359 auto children = priv->children;
1360 std::for_each (children.begin(), children.end(), xaccFreeOneChildAccount);
1361
1362 /* The foreach should have removed all the children already. */
1363 priv->children.clear();
1364}
1365
1366/* The xaccFreeAccount() routine releases memory associated with the
1367 * account. It should never be called directly from user code;
1368 * instead, the xaccAccountDestroy() routine should be used (because
1369 * xaccAccountDestroy() has the correct commit semantics). */
1370static void
1371xaccFreeAccount (Account *acc)
1372{
1373 AccountPrivate *priv;
1374 GList *lp;
1375
1376 g_return_if_fail(GNC_IS_ACCOUNT(acc));
1377
1378 priv = GET_PRIVATE(acc);
1379 qof_event_gen (&acc->inst, QOF_EVENT_DESTROY, nullptr);
1380
1381 /* Otherwise the lists below get munged while we're iterating
1382 * them, possibly crashing.
1383 */
1384 if (!qof_instance_get_destroying (acc))
1385 qof_instance_set_destroying(acc, TRUE);
1386
1387 if (!priv->children.empty())
1388 {
1389 PERR (" instead of calling xaccFreeAccount(), please call\n"
1390 " xaccAccountBeginEdit(); xaccAccountDestroy();\n");
1391
1392 /* First, recursively free children, also frees list */
1393 xaccFreeAccountChildren(acc);
1394 }
1395
1396 /* remove lots -- although these should be gone by now. */
1397 if (priv->lots)
1398 {
1399 PERR (" instead of calling xaccFreeAccount(), please call\n"
1400 " xaccAccountBeginEdit(); xaccAccountDestroy();\n");
1401
1402 for (lp = priv->lots; lp; lp = lp->next)
1403 {
1404 GNCLot *lot = static_cast<GNCLot*>(lp->data);
1405 gnc_lot_destroy (lot);
1406 }
1407 g_list_free (priv->lots);
1408 priv->lots = nullptr;
1409 }
1410
1411 /* Next, clean up the splits */
1412 /* NB there shouldn't be any splits by now ... they should
1413 * have been all been freed by CommitEdit(). We can remove this
1414 * check once we know the warning isn't occurring any more. */
1415 if (!priv->splits.empty())
1416 {
1417 PERR (" instead of calling xaccFreeAccount(), please call\n"
1418 " xaccAccountBeginEdit(); xaccAccountDestroy();\n");
1419
1420 qof_instance_reset_editlevel(acc);
1421
1422 for (auto s : priv->splits)
1423 {
1424 g_assert(xaccSplitGetAccount(s) == acc);
1425 xaccSplitDestroy (s);
1426 }
1427/* Nothing here (or in xaccAccountCommitEdit) nullptrs priv->splits, so this asserts every time.
1428 g_assert(priv->splits == nullptr);
1429*/
1430 }
1431
1432 qof_string_cache_remove(priv->accountName);
1433 qof_string_cache_remove(priv->accountCode);
1434 qof_string_cache_remove(priv->description);
1435 priv->accountName = priv->accountCode = priv->description = nullptr;
1436
1437 /* zero out values, just in case stray
1438 * pointers are pointing here. */
1439
1440 priv->last_num = nullptr;
1441 priv->tax_us_code = nullptr;
1442 priv->tax_us_pns = nullptr;
1443 priv->color = nullptr;
1444 priv->sort_order = nullptr;
1445 priv->notes = nullptr;
1446 priv->filter = nullptr;
1447
1448 priv->parent = nullptr;
1449
1450 priv->balance = gnc_numeric_zero();
1451 priv->noclosing_balance = gnc_numeric_zero();
1452 priv->cleared_balance = gnc_numeric_zero();
1453 priv->reconciled_balance = gnc_numeric_zero();
1454
1455 priv->type = ACCT_TYPE_NONE;
1456 gnc_commodity_decrement_usage_count(priv->commodity);
1457 priv->commodity = nullptr;
1458
1459 priv->balance_dirty = FALSE;
1460 priv->has_stock_split = false;
1461 priv->sort_dirty = FALSE;
1462 priv->splits.~SplitsVec();
1463 priv->children.~AccountVec();
1464 g_hash_table_destroy (priv->splits_hash);
1465
1466 /* qof_instance_release (&acc->inst); */
1467 g_object_unref(acc);
1468}
1469
1470/********************************************************************\
1471 * transactional routines
1472\********************************************************************/
1473
1474void
1476{
1477 g_return_if_fail(acc);
1478 qof_begin_edit(&acc->inst);
1479}
1480
1481static void on_done(QofInstance *inst)
1482{
1483 /* old event style */
1484 qof_event_gen (inst, QOF_EVENT_MODIFY, nullptr);
1485}
1486
1487static void on_err (QofInstance *inst, QofBackendError errcode)
1488{
1489 PERR("commit error: %d", errcode);
1490 gnc_engine_signal_commit_error( errcode );
1491}
1492
1493static void acc_free (QofInstance *inst)
1494{
1495 AccountPrivate *priv;
1496 Account *acc = (Account *) inst;
1497
1498 priv = GET_PRIVATE(acc);
1499 if (priv->parent)
1500 gnc_account_remove_child(priv->parent, acc);
1501 xaccFreeAccount(acc);
1502}
1503
1504static void
1505destroy_pending_splits_for_account(QofInstance *ent, gpointer acc)
1506{
1507 Transaction *trans = (Transaction *) ent;
1508 Split *split;
1509
1510 if (xaccTransIsOpen(trans))
1511 while ((split = xaccTransFindSplitByAccount(trans, static_cast<Account*>(acc))))
1512 xaccSplitDestroy(split);
1513}
1514
1515void
1517{
1518 AccountPrivate *priv;
1519 QofBook *book;
1520
1521 g_return_if_fail(acc);
1522 if (!qof_commit_edit(&acc->inst)) return;
1523
1524 /* If marked for deletion, get rid of subaccounts first,
1525 * and then the splits ... */
1526 priv = GET_PRIVATE(acc);
1528 {
1529 QofCollection *col;
1530
1531 qof_instance_increase_editlevel(acc);
1532
1533 /* First, recursively free children */
1534 xaccFreeAccountChildren(acc);
1535
1536 PINFO ("freeing splits for account %p (%s)",
1537 acc, priv->accountName ? priv->accountName : "(null)");
1538
1539 book = qof_instance_get_book(acc);
1540
1541 /* If book is shutting down, just clear the split list. The splits
1542 themselves will be destroyed by the transaction code */
1543 if (!qof_book_shutting_down(book))
1544 {
1545 // We need to delete in reverse order so that the vector's iterators aren't invalidated.
1546 for_each(priv->splits.rbegin(), priv->splits.rend(), [](Split *s) {
1547 xaccSplitDestroy (s); });
1548 }
1549 else
1550 {
1551 priv->splits.clear();
1552 g_hash_table_remove_all (priv->splits_hash);
1553 }
1554
1555 /* It turns out there's a case where this assertion does not hold:
1556 When the user tries to delete an Imbalance account, while also
1557 deleting all the splits in it. The splits will just get
1558 recreated and put right back into the same account!
1559
1560 g_assert(priv->splits == nullptr || qof_book_shutting_down(acc->inst.book));
1561 */
1562
1563 if (!qof_book_shutting_down(book))
1564 {
1565 col = qof_book_get_collection(book, GNC_ID_TRANS);
1566 qof_collection_foreach(col, destroy_pending_splits_for_account, acc);
1567
1568 /* the lots should be empty by now */
1569 for (auto lp = priv->lots; lp; lp = lp->next)
1570 {
1571 GNCLot *lot = static_cast<GNCLot*>(lp->data);
1572 gnc_lot_destroy (lot);
1573 }
1574 }
1575 g_list_free(priv->lots);
1576 priv->lots = nullptr;
1577
1578 qof_instance_set_dirty(&acc->inst);
1579 qof_instance_decrease_editlevel(acc);
1580 }
1581 else
1582 {
1583 xaccAccountBringUpToDate(acc);
1584 }
1585
1586 qof_commit_edit_part2(&acc->inst, on_err, on_done, acc_free);
1587}
1588
1589void
1591{
1592 g_return_if_fail(GNC_IS_ACCOUNT(acc));
1593
1594 qof_instance_set_destroying(acc, TRUE);
1595
1597}
1598
1599void
1601{
1602 auto priv = GET_PRIVATE(acc);
1603 std::vector<Transaction*> transactions;
1604 transactions.reserve(priv->splits.size());
1605 std::transform(priv->splits.begin(), priv->splits.end(),
1606 back_inserter(transactions),
1607 [](auto split) { return split->parent; });
1608 std::stable_sort(transactions.begin(), transactions.end());
1609 transactions.erase(std::unique(transactions.begin(), transactions.end()),
1610 transactions.end());
1612 std::for_each(transactions.rbegin(), transactions.rend(),
1613 [](auto trans) { xaccTransDestroy (trans); });
1615}
1616
1617/********************************************************************\
1618\********************************************************************/
1619
1620static gboolean
1621xaccAcctChildrenEqual(const AccountVec& na,
1622 const AccountVec& nb,
1623 gboolean check_guids)
1624{
1625 if (na.size() != nb.size())
1626 {
1627 PINFO ("Accounts have different numbers of children");
1628 return (FALSE);
1629 }
1630
1631 for (auto aa : na)
1632 {
1633 auto it_b = std::find_if (nb.begin(), nb.end(), [aa](auto ab) -> bool
1634 {
1635 if (!aa) return (!ab);
1636 if (!ab) return false;
1637 auto code_a{GET_PRIVATE(aa)->accountCode};
1638 auto code_b{GET_PRIVATE(ab)->accountCode};
1639 if ((code_a && *code_a) || (code_b && *code_b)) return !g_strcmp0 (code_a, code_b);
1640 return !g_strcmp0 (GET_PRIVATE(aa)->accountName, GET_PRIVATE(ab)->accountName);
1641 });
1642
1643 if (it_b == nb.end())
1644 {
1645 PINFO ("Unable to find matching child account.");
1646 return FALSE;
1647 }
1648 else if (auto ab = *it_b; !xaccAccountEqual(aa, ab, check_guids))
1649 {
1650 char sa[GUID_ENCODING_LENGTH + 1];
1651 char sb[GUID_ENCODING_LENGTH + 1];
1652
1655
1656 PWARN ("accounts %s and %s differ", sa, sb);
1657
1658 return(FALSE);
1659 }
1660 }
1661
1662 return(TRUE);
1663}
1664
1665gboolean
1666xaccAccountEqual(const Account *aa, const Account *ab, gboolean check_guids)
1667{
1668 AccountPrivate *priv_aa, *priv_ab;
1669
1670 if (!aa && !ab) return TRUE;
1671
1672 g_return_val_if_fail(GNC_IS_ACCOUNT(aa), FALSE);
1673 g_return_val_if_fail(GNC_IS_ACCOUNT(ab), FALSE);
1674
1675 priv_aa = GET_PRIVATE(aa);
1676 priv_ab = GET_PRIVATE(ab);
1677 if (priv_aa->type != priv_ab->type)
1678 {
1679 PWARN ("types differ: %d vs %d", priv_aa->type, priv_ab->type);
1680 return FALSE;
1681 }
1682
1683 if (g_strcmp0(priv_aa->accountName, priv_ab->accountName) != 0)
1684 {
1685 PWARN ("names differ: %s vs %s", priv_aa->accountName, priv_ab->accountName);
1686 return FALSE;
1687 }
1688
1689 if (g_strcmp0(priv_aa->accountCode, priv_ab->accountCode) != 0)
1690 {
1691 PWARN ("codes differ: %s vs %s", priv_aa->accountCode, priv_ab->accountCode);
1692 return FALSE;
1693 }
1694
1695 if (g_strcmp0(priv_aa->description, priv_ab->description) != 0)
1696 {
1697 PWARN ("descriptions differ: %s vs %s", priv_aa->description, priv_ab->description);
1698 return FALSE;
1699 }
1700
1701 if (!gnc_commodity_equal(priv_aa->commodity, priv_ab->commodity))
1702 {
1703 PWARN ("commodities differ");
1704 return FALSE;
1705 }
1706
1707 if (check_guids)
1708 {
1709 if (qof_instance_guid_compare(aa, ab) != 0)
1710 {
1711 PWARN ("GUIDs differ");
1712 return FALSE;
1713 }
1714 }
1715
1716 if (qof_instance_compare_kvp (QOF_INSTANCE (aa), QOF_INSTANCE (ab)) != 0)
1717 {
1718 char *frame_a;
1719 char *frame_b;
1720
1721 frame_a = qof_instance_kvp_as_string (QOF_INSTANCE (aa));
1722 frame_b = qof_instance_kvp_as_string (QOF_INSTANCE (ab));
1723
1724 PWARN ("kvp frames differ:\n%s\n\nvs\n\n%s", frame_a, frame_b);
1725
1726 g_free (frame_a);
1727 g_free (frame_b);
1728
1729 return FALSE;
1730 }
1731
1732 if (!gnc_numeric_equal(priv_aa->starting_balance, priv_ab->starting_balance))
1733 {
1734 char *str_a;
1735 char *str_b;
1736
1737 str_a = gnc_numeric_to_string(priv_aa->starting_balance);
1738 str_b = gnc_numeric_to_string(priv_ab->starting_balance);
1739
1740 PWARN ("starting balances differ: %s vs %s", str_a, str_b);
1741
1742 g_free (str_a);
1743 g_free (str_b);
1744
1745 return FALSE;
1746 }
1747
1748 if (!gnc_numeric_equal(priv_aa->starting_noclosing_balance,
1749 priv_ab->starting_noclosing_balance))
1750 {
1751 char *str_a;
1752 char *str_b;
1753
1754 str_a = gnc_numeric_to_string(priv_aa->starting_noclosing_balance);
1755 str_b = gnc_numeric_to_string(priv_ab->starting_noclosing_balance);
1756
1757 PWARN ("starting noclosing balances differ: %s vs %s", str_a, str_b);
1758
1759 g_free (str_a);
1760 g_free (str_b);
1761
1762 return FALSE;
1763 }
1764 if (!gnc_numeric_equal(priv_aa->starting_cleared_balance,
1765 priv_ab->starting_cleared_balance))
1766 {
1767 char *str_a;
1768 char *str_b;
1769
1770 str_a = gnc_numeric_to_string(priv_aa->starting_cleared_balance);
1771 str_b = gnc_numeric_to_string(priv_ab->starting_cleared_balance);
1772
1773 PWARN ("starting cleared balances differ: %s vs %s", str_a, str_b);
1774
1775 g_free (str_a);
1776 g_free (str_b);
1777
1778 return FALSE;
1779 }
1780
1781 if (!gnc_numeric_equal(priv_aa->starting_reconciled_balance,
1782 priv_ab->starting_reconciled_balance))
1783 {
1784 char *str_a;
1785 char *str_b;
1786
1787 str_a = gnc_numeric_to_string(priv_aa->starting_reconciled_balance);
1788 str_b = gnc_numeric_to_string(priv_ab->starting_reconciled_balance);
1789
1790 PWARN ("starting reconciled balances differ: %s vs %s", str_a, str_b);
1791
1792 g_free (str_a);
1793 g_free (str_b);
1794
1795 return FALSE;
1796 }
1797
1798 if (!gnc_numeric_equal(priv_aa->balance, priv_ab->balance))
1799 {
1800 char *str_a;
1801 char *str_b;
1802
1803 str_a = gnc_numeric_to_string(priv_aa->balance);
1804 str_b = gnc_numeric_to_string(priv_ab->balance);
1805
1806 PWARN ("balances differ: %s vs %s", str_a, str_b);
1807
1808 g_free (str_a);
1809 g_free (str_b);
1810
1811 return FALSE;
1812 }
1813
1814 if (!gnc_numeric_equal(priv_aa->noclosing_balance, priv_ab->noclosing_balance))
1815 {
1816 char *str_a;
1817 char *str_b;
1818
1819 str_a = gnc_numeric_to_string(priv_aa->noclosing_balance);
1820 str_b = gnc_numeric_to_string(priv_ab->noclosing_balance);
1821
1822 PWARN ("noclosing balances differ: %s vs %s", str_a, str_b);
1823
1824 g_free (str_a);
1825 g_free (str_b);
1826
1827 return FALSE;
1828 }
1829 if (!gnc_numeric_equal(priv_aa->cleared_balance, priv_ab->cleared_balance))
1830 {
1831 char *str_a;
1832 char *str_b;
1833
1834 str_a = gnc_numeric_to_string(priv_aa->cleared_balance);
1835 str_b = gnc_numeric_to_string(priv_ab->cleared_balance);
1836
1837 PWARN ("cleared balances differ: %s vs %s", str_a, str_b);
1838
1839 g_free (str_a);
1840 g_free (str_b);
1841
1842 return FALSE;
1843 }
1844
1845 if (!gnc_numeric_equal(priv_aa->reconciled_balance, priv_ab->reconciled_balance))
1846 {
1847 char *str_a;
1848 char *str_b;
1849
1850 str_a = gnc_numeric_to_string(priv_aa->reconciled_balance);
1851 str_b = gnc_numeric_to_string(priv_ab->reconciled_balance);
1852
1853 PWARN ("reconciled balances differ: %s vs %s", str_a, str_b);
1854
1855 g_free (str_a);
1856 g_free (str_b);
1857
1858 return FALSE;
1859 }
1860
1861 /* no parent; always compare downwards. */
1862
1863 if (!std::equal (priv_aa->splits.begin(), priv_aa->splits.end(),
1864 priv_ab->splits.begin(), priv_ab->splits.end(),
1865 [check_guids](auto sa, auto sb)
1866 { return xaccSplitEqual(sa, sb, check_guids, true, false); }))
1867 {
1868 PWARN ("splits differ");
1869 return false;
1870 }
1871
1872 if (!xaccAcctChildrenEqual(priv_aa->children, priv_ab->children, check_guids))
1873 {
1874 PWARN ("children differ");
1875 return FALSE;
1876 }
1877
1878 return(TRUE);
1879}
1880
1881/********************************************************************\
1882\********************************************************************/
1883void
1885{
1886 AccountPrivate *priv;
1887
1888 g_return_if_fail(GNC_IS_ACCOUNT(acc));
1889
1891 return;
1892
1893 priv = GET_PRIVATE(acc);
1894 priv->sort_dirty = TRUE;
1895}
1896
1897void
1899{
1900 AccountPrivate *priv;
1901
1902 g_return_if_fail(GNC_IS_ACCOUNT(acc));
1903
1905 return;
1906
1907 priv = GET_PRIVATE(acc);
1908 priv->balance_dirty = TRUE;
1909}
1910
1912{
1913 AccountPrivate *priv;
1914
1915 g_return_if_fail (GNC_IS_ACCOUNT (acc));
1916
1918 return;
1919
1920 priv = GET_PRIVATE (acc);
1921 priv->defer_bal_computation = defer;
1922}
1923
1925{
1926 AccountPrivate *priv;
1927 if (!acc)
1928 return false;
1929 priv = GET_PRIVATE (acc);
1930 return priv->defer_bal_computation;
1931}
1932
1933
1934/********************************************************************\
1935\********************************************************************/
1936
1937static bool split_cmp_less (const Split* a, const Split* b)
1938{
1939 return xaccSplitOrder (a, b) < 0;
1940}
1941
1942gboolean
1944{
1945 AccountPrivate *priv;
1946
1947 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
1948 g_return_val_if_fail(GNC_IS_SPLIT(s), FALSE);
1949
1950 priv = GET_PRIVATE(acc);
1951 if (!g_hash_table_add (priv->splits_hash, s))
1952 return false;
1953
1954 priv->splits.push_back (s);
1955
1956 if (qof_instance_get_editlevel(acc) == 0)
1957 std::sort (priv->splits.begin(), priv->splits.end(), split_cmp_less);
1958 else
1959 priv->sort_dirty = true;
1960
1961 //FIXME: find better event
1962 qof_event_gen (&acc->inst, QOF_EVENT_MODIFY, nullptr);
1963 /* Also send an event based on the account */
1964 qof_event_gen(&acc->inst, GNC_EVENT_ITEM_ADDED, s);
1965
1966 priv->balance_dirty = TRUE;
1967// DRH: Should the below be added? It is present in the delete path.
1968// xaccAccountRecomputeBalance(acc);
1969 return TRUE;
1970}
1971
1972gboolean
1974{
1975 AccountPrivate *priv;
1976
1977 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
1978 g_return_val_if_fail(GNC_IS_SPLIT(s), FALSE);
1979
1980 priv = GET_PRIVATE(acc);
1981
1982 if (!g_hash_table_remove (priv->splits_hash, s))
1983 return false;
1984
1985 // shortcut pruning the last element. this is the most common
1986 // remove_split operation during UI or book shutdown.
1987 if (s == priv->splits.back())
1988 priv->splits.pop_back();
1989 else
1990 priv->splits.erase (std::remove (priv->splits.begin(), priv->splits.end(), s),
1991 priv->splits.end());
1992
1993 //FIXME: find better event type
1994 qof_event_gen(&acc->inst, QOF_EVENT_MODIFY, nullptr);
1995 // And send the account-based event, too
1996 qof_event_gen(&acc->inst, GNC_EVENT_ITEM_REMOVED, s);
1997
1998 priv->balance_dirty = TRUE;
2000 return TRUE;
2001}
2002
2003void
2004xaccAccountSortSplits (Account *acc, gboolean force)
2005{
2006 AccountPrivate *priv;
2007
2008 g_return_if_fail(GNC_IS_ACCOUNT(acc));
2009
2010 priv = GET_PRIVATE(acc);
2011 if (!priv->sort_dirty || (!force && qof_instance_get_editlevel(acc) > 0))
2012 return;
2013 std::sort (priv->splits.begin(), priv->splits.end(), split_cmp_less);
2014 priv->sort_dirty = FALSE;
2015 priv->balance_dirty = TRUE;
2016}
2017
2018static void
2019xaccAccountBringUpToDate(Account *acc)
2020{
2021 if (!acc) return;
2022
2023 /* if a re-sort happens here, then everything will update, so the
2024 cost basis and balance calls are no-ops */
2025 xaccAccountSortSplits(acc, FALSE);
2027}
2028
2029/********************************************************************\
2030\********************************************************************/
2031
2032void
2033xaccAccountSetGUID (Account *acc, const GncGUID *guid)
2034{
2035 g_return_if_fail(GNC_IS_ACCOUNT(acc));
2036 g_return_if_fail(guid);
2037
2038 /* XXX this looks fishy and weird to me ... */
2039 PINFO("acct=%p", acc);
2041 qof_instance_set_guid (&acc->inst, guid);
2042 qof_instance_set_dirty(&acc->inst);
2044}
2045
2046/********************************************************************\
2047\********************************************************************/
2048
2049Account *
2051{
2052 QofCollection *col;
2053 if (!guid || !book) return nullptr;
2054 col = qof_book_get_collection (book, GNC_ID_ACCOUNT);
2055 return (Account *) qof_collection_lookup_entity (col, guid);
2056}
2057
2058/********************************************************************\
2059\********************************************************************/
2060
2061void
2063{
2064 AccountPrivate *priv;
2065
2066 g_return_if_fail(GNC_IS_ACCOUNT(acc));
2067
2068 priv = GET_PRIVATE(acc);
2069 priv->mark = m;
2070}
2071
2072void
2073xaccClearMark (Account *acc, short val)
2074{
2075 Account *root;
2076
2077 g_return_if_fail(GNC_IS_ACCOUNT(acc));
2078
2079 root = gnc_account_get_root(acc);
2080 xaccClearMarkDown(root ? root : acc, val);
2081}
2082
2083void
2085{
2086 AccountPrivate *priv;
2087
2088 g_return_if_fail(GNC_IS_ACCOUNT(acc));
2089
2090 priv = GET_PRIVATE(acc);
2091 priv->mark = val;
2092 std::for_each (priv->children.begin(), priv->children.end(),
2093 [val](auto acc){ xaccClearMarkDown(acc, val); });
2094}
2095
2096/********************************************************************\
2097\********************************************************************/
2098
2099GNCPolicy *
2101{
2102 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
2103
2104 return GET_PRIVATE(acc)->policy;
2105}
2106
2107void
2108gnc_account_set_policy (Account *acc, GNCPolicy *policy)
2109{
2110 AccountPrivate *priv;
2111
2112 g_return_if_fail(GNC_IS_ACCOUNT(acc));
2113
2114 priv = GET_PRIVATE(acc);
2115 priv->policy = policy ? policy : xaccGetFIFOPolicy();
2116}
2117
2118/********************************************************************\
2119\********************************************************************/
2120
2121void
2122xaccAccountRemoveLot (Account *acc, GNCLot *lot)
2123{
2124 AccountPrivate *priv;
2125
2126 g_return_if_fail(GNC_IS_ACCOUNT(acc));
2127 g_return_if_fail(GNC_IS_LOT(lot));
2128
2129 priv = GET_PRIVATE(acc);
2130 g_return_if_fail(priv->lots);
2131
2132 ENTER ("(acc=%p, lot=%p)", acc, lot);
2133 priv->lots = g_list_remove(priv->lots, lot);
2134 qof_event_gen (QOF_INSTANCE(lot), QOF_EVENT_REMOVE, nullptr);
2135 qof_event_gen (&acc->inst, QOF_EVENT_MODIFY, nullptr);
2136 LEAVE ("(acc=%p, lot=%p)", acc, lot);
2137}
2138
2139void
2141{
2142 /* errors */
2143 g_return_if_fail(GNC_IS_ACCOUNT(acc));
2144 g_return_if_fail(GNC_IS_LOT(lot));
2145
2146 /* optimizations */
2147 auto lot_acc = gnc_lot_get_account(lot);
2148 if (lot_acc == acc)
2149 return;
2150
2151 ENTER ("(acc=%p, lot=%p)", acc, lot);
2152
2153 /* pull it out of the old account */
2154 if (lot_acc)
2155 {
2156 auto priv = GET_PRIVATE(lot_acc);
2157 priv->lots = g_list_remove(priv->lots, lot);
2158 qof_event_gen (&lot_acc->inst, QOF_EVENT_MODIFY, nullptr);
2159 }
2160
2161 auto priv = GET_PRIVATE(acc);
2162 priv->lots = g_list_prepend(priv->lots, lot);
2163 gnc_lot_set_account(lot, acc);
2164
2165 /* Don't move the splits to the new account. The caller will do this
2166 * if appropriate, and doing it here will not work if we are being
2167 * called from gnc_book_close_period since xaccAccountInsertSplit
2168 * will try to balance capital gains and things aren't ready for that. */
2169
2170 qof_event_gen (QOF_INSTANCE(lot), QOF_EVENT_ADD, nullptr);
2171 qof_event_gen (&acc->inst, QOF_EVENT_MODIFY, nullptr);
2172
2173 LEAVE ("(acc=%p, lot=%p)", acc, lot);
2174}
2175
2176/********************************************************************\
2177\********************************************************************/
2178static void
2179xaccPreSplitMove (Split *split)
2180{
2182}
2183
2184static void
2185xaccPostSplitMove (Split *split, Account *accto)
2186{
2187 Transaction *trans;
2188
2189 xaccSplitSetAccount(split, accto);
2190 xaccSplitSetAmount(split, split->amount);
2191 trans = xaccSplitGetParent (split);
2192 xaccTransCommitEdit (trans);
2193}
2194
2195void
2197{
2198 AccountPrivate *from_priv;
2199
2200 /* errors */
2201 g_return_if_fail(GNC_IS_ACCOUNT(accfrom));
2202 g_return_if_fail(GNC_IS_ACCOUNT(accto));
2203
2204 /* optimizations */
2205 from_priv = GET_PRIVATE(accfrom);
2206 if (from_priv->splits.empty() || accfrom == accto)
2207 return;
2208
2209 /* check for book mix-up */
2210 g_return_if_fail (qof_instance_books_equal(accfrom, accto));
2211 ENTER ("(accfrom=%p, accto=%p)", accfrom, accto);
2212
2213 xaccAccountBeginEdit(accfrom);
2214 xaccAccountBeginEdit(accto);
2215 /* Begin editing both accounts and all transactions in accfrom. */
2216 std::for_each (from_priv->splits.begin(), from_priv->splits.end(), xaccPreSplitMove);
2217
2218 /* Concatenate accfrom's lists of splits and lots to accto's lists. */
2219 //to_priv->splits = g_list_concat(to_priv->splits, from_priv->splits);
2220 //to_priv->lots = g_list_concat(to_priv->lots, from_priv->lots);
2221
2222 /* Set appropriate flags. */
2223 //from_priv->balance_dirty = TRUE;
2224 //from_priv->sort_dirty = FALSE;
2225 //to_priv->balance_dirty = TRUE;
2226 //to_priv->sort_dirty = TRUE;
2227
2228 /*
2229 * Change each split's account back pointer to accto.
2230 * Convert each split's amount to accto's commodity.
2231 * Commit to editing each transaction.
2232 */
2233 auto splits = from_priv->splits;
2234 std::for_each (splits.begin(), splits.end(), [accto](auto s){ xaccPostSplitMove (s, accto); });
2235
2236 /* Finally empty accfrom. */
2237 g_assert(from_priv->splits.empty());
2238 g_assert(from_priv->lots == nullptr);
2239 xaccAccountCommitEdit(accfrom);
2240 xaccAccountCommitEdit(accto);
2241
2242 LEAVE ("(accfrom=%p, accto=%p)", accfrom, accto);
2243}
2244
2245
2246/********************************************************************\
2247 * xaccAccountRecomputeBalance *
2248 * recomputes the partial balances and the current balance for *
2249 * this account. *
2250 * *
2251 * The way the computation is done depends on whether the partial *
2252 * balances are for a monetary account (bank, cash, etc.) or a *
2253 * certificate account (stock portfolio, mutual fund). For bank *
2254 * accounts, the invariant amount is the dollar amount. For share *
2255 * accounts, the invariant amount is the number of shares. For *
2256 * share accounts, the share price fluctuates, and the current *
2257 * value of such an account is the number of shares times the *
2258 * current share price. *
2259 * *
2260 * Part of the complexity of this computation stems from the fact *
2261 * xacc uses a double-entry system, meaning that one transaction *
2262 * appears in two accounts: one account is debited, and the other *
2263 * is credited. When the transaction represents a sale of shares, *
2264 * or a purchase of shares, some care must be taken to compute *
2265 * balances correctly. For a sale of shares, the stock account must*
2266 * be debited in shares, but the bank account must be credited *
2267 * in dollars. Thus, two different mechanisms must be used to *
2268 * compute balances, depending on account type. *
2269 * *
2270 * Args: account -- the account for which to recompute balances *
2271 * Return: void *
2272\********************************************************************/
2273
2274void
2276{
2277 if (nullptr == acc) return;
2278
2279 auto priv = GET_PRIVATE(acc);
2280 if (qof_instance_get_editlevel(acc) > 0) return;
2281 if (!priv->balance_dirty || priv->defer_bal_computation) return;
2282 if (qof_instance_get_destroying(acc)) return;
2284
2285 auto balance = priv->starting_balance;
2286 auto noclosing_balance = priv->starting_noclosing_balance;
2287 auto cleared_balance = priv->starting_cleared_balance;
2288 auto reconciled_balance = priv->starting_reconciled_balance;
2289 auto has_stock_split = false;
2290
2291 PINFO ("acct=%s starting baln=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT,
2292 priv->accountName, balance.num, balance.denom);
2293 for (auto split : priv->splits)
2294 {
2295 auto amt = xaccSplitGetAmount (split);
2296
2297 if (xaccSplitIsStockSplit(split))
2298 {
2299 if (gnc_numeric_zero_p(balance))
2300 continue;
2301 auto new_balance = gnc_numeric_add_fixed(balance, amt);
2302 if (gnc_numeric_zero_p(new_balance))
2303 continue;
2304 auto ratio = gnc_numeric_div(new_balance, balance, GNC_DENOM_AUTO,
2306 auto denom = xaccAccountGetCommoditySCU(acc);
2307 for (auto psplit : priv->splits)
2308 {
2309 if (psplit == split)
2310 break;
2312 gnc_numeric_mul(xaccSplitGetAdjustedAmount(psplit), ratio, denom,
2314 }
2315 balance = new_balance;
2316 has_stock_split = true;
2317 }
2318 else
2319 {
2320 split->adjusted_amount = amt;
2321 balance = gnc_numeric_add_fixed(balance, amt);
2322 }
2323
2324 if (NREC != split->reconciled)
2325 {
2326 cleared_balance = gnc_numeric_add_fixed(cleared_balance, amt);
2327 }
2328
2329 if (YREC == split->reconciled ||
2330 FREC == split->reconciled)
2331 {
2332 reconciled_balance =
2333 gnc_numeric_add_fixed(reconciled_balance, amt);
2334 }
2335
2336 if (!(xaccTransGetIsClosingTxn (split->parent)))
2337 noclosing_balance = gnc_numeric_add_fixed(noclosing_balance, amt);
2338
2339 split->balance = balance;
2340 split->noclosing_balance = noclosing_balance;
2341 split->cleared_balance = cleared_balance;
2342 split->reconciled_balance = reconciled_balance;
2343
2344 }
2345
2346 priv->balance = balance;
2347 priv->noclosing_balance = noclosing_balance;
2348 priv->cleared_balance = cleared_balance;
2349 priv->reconciled_balance = reconciled_balance;
2350 priv->balance_dirty = FALSE;
2351 priv->has_stock_split = has_stock_split;
2352}
2353
2354/********************************************************************\
2355\********************************************************************/
2356
2357/* The sort order is used to implicitly define an
2358 * order for report generation */
2359
2360static int typeorder[NUM_ACCOUNT_TYPES] =
2361{
2366};
2367
2368static int revorder[NUM_ACCOUNT_TYPES] =
2369{
2370 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
2371};
2372
2373
2374int
2375xaccAccountOrder (const Account *aa, const Account *ab)
2376{
2377 AccountPrivate *priv_aa, *priv_ab;
2378 const char *da, *db;
2379 int ta, tb, result;
2380
2381 if (aa == ab) return 0;
2382 if (!ab) return -1;
2383 if (!aa) return +1;
2384
2385 priv_aa = GET_PRIVATE(aa);
2386 priv_ab = GET_PRIVATE(ab);
2387
2388 /* sort on accountCode strings */
2389 da = priv_aa->accountCode;
2390 db = priv_ab->accountCode;
2391
2392 /* Otherwise do a string sort */
2393 result = g_strcmp0 (da, db);
2394 if (result)
2395 return result;
2396
2397 /* if account-type-order array not initialized, initialize it */
2398 /* this will happen at most once during program invocation */
2399 if (-1 == revorder[0])
2400 {
2401 int i;
2402 for (i = 0; i < NUM_ACCOUNT_TYPES; i++)
2403 {
2404 revorder [typeorder[i]] = i;
2405 }
2406 }
2407
2408 /* otherwise, sort on account type */
2409 ta = priv_aa->type;
2410 tb = priv_ab->type;
2411 ta = revorder[ta];
2412 tb = revorder[tb];
2413 if (ta < tb) return -1;
2414 if (ta > tb) return +1;
2415
2416 /* otherwise, sort on accountName strings */
2417 da = priv_aa->accountName;
2418 db = priv_ab->accountName;
2419 result = safe_utf8_collate(da, db);
2420 if (result)
2421 return result;
2422
2423 /* guarantee a stable sort */
2424 return qof_instance_guid_compare(aa, ab);
2425}
2426
2427static int
2428qof_xaccAccountOrder (const Account **aa, const Account **ab)
2429{
2430 return xaccAccountOrder(*aa, *ab);
2431}
2432
2433/********************************************************************\
2434\********************************************************************/
2435
2436void
2438{
2439 AccountPrivate *priv;
2440
2441 /* errors */
2442 g_return_if_fail(GNC_IS_ACCOUNT(acc));
2443 g_return_if_fail(tip < NUM_ACCOUNT_TYPES);
2444
2445 /* optimizations */
2446 priv = GET_PRIVATE(acc);
2447 if (priv->type == tip)
2448 return;
2449
2451 priv->type = tip;
2452 priv->balance_dirty = TRUE; /* new type may affect balance computation */
2453 mark_account(acc);
2455}
2456
2457void
2458xaccAccountSetName (Account *acc, const char *str)
2459{
2460 AccountPrivate *priv;
2461
2462 /* errors */
2463 g_return_if_fail(GNC_IS_ACCOUNT(acc));
2464 g_return_if_fail(str);
2465
2466 /* optimizations */
2467 priv = GET_PRIVATE(acc);
2468 if (g_strcmp0(str, priv->accountName) == 0)
2469 return;
2470
2472 priv->accountName = qof_string_cache_replace(priv->accountName, str);
2473 mark_account (acc);
2475}
2476
2477void
2478xaccAccountSetCode (Account *acc, const char *str)
2479{
2480 AccountPrivate *priv;
2481
2482 /* errors */
2483 g_return_if_fail(GNC_IS_ACCOUNT(acc));
2484
2485 /* optimizations */
2486 priv = GET_PRIVATE(acc);
2487 if (g_strcmp0(str, priv->accountCode) == 0)
2488 return;
2489
2491 priv->accountCode = qof_string_cache_replace(priv->accountCode, str ? str : "");
2492 mark_account (acc);
2494}
2495
2496void
2497xaccAccountSetDescription (Account *acc, const char *str)
2498{
2499 AccountPrivate *priv;
2500
2501 /* errors */
2502 g_return_if_fail(GNC_IS_ACCOUNT(acc));
2503
2504 /* optimizations */
2505 priv = GET_PRIVATE(acc);
2506 if (g_strcmp0(str, priv->description) == 0)
2507 return;
2508
2510 priv->description = qof_string_cache_replace(priv->description, str ? str : "");
2511 mark_account (acc);
2513}
2514
2515static void
2516set_kvp_gnc_numeric_path (Account *acc, const std::vector<std::string>& path,
2517 std::optional<gnc_numeric> value)
2518{
2520 qof_instance_set_path_kvp<gnc_numeric> (QOF_INSTANCE(acc), value, path);
2522}
2523
2524static std::optional<gnc_numeric>
2525get_kvp_gnc_numeric_path (const Account *acc, const Path& path)
2526{
2527 return qof_instance_get_path_kvp<gnc_numeric> (QOF_INSTANCE(acc), path);
2528}
2529
2530static void
2531set_kvp_string_path (Account *acc, std::vector<std::string> const & path,
2532 const char *value)
2533{
2534 std::optional<const char*> val;
2535 if (value && *value)
2536 val = g_strdup(value);
2537
2539 qof_instance_set_path_kvp<const char*> (QOF_INSTANCE(acc), val, path);
2541}
2542
2543static const char*
2544get_kvp_string_path (const Account *acc, const Path& path)
2545{
2546 auto rv{qof_instance_get_path_kvp<const char*> (QOF_INSTANCE(acc), path)};
2547 return rv ? *rv : nullptr;
2548}
2549
2550static void
2551set_kvp_account_path (Account* acc, const Path& path, const Account* kvp_account)
2552{
2553 std::optional<GncGUID*> val;
2554 if (kvp_account)
2555 val = guid_copy(xaccAccountGetGUID (kvp_account));
2556
2558 qof_instance_set_path_kvp<GncGUID*> (QOF_INSTANCE(acc), val, path);
2560}
2561
2562static Account*
2563get_kvp_account_path (const Account *acc, const Path& path)
2564{
2565 auto val{qof_instance_get_path_kvp<GncGUID*> (QOF_INSTANCE(acc), path)};
2566 return val ? xaccAccountLookup (*val, gnc_account_get_book (acc)) : nullptr;
2567}
2568
2569static void
2570set_kvp_boolean_path (Account *acc, const Path& path, gboolean option)
2571{
2572 set_kvp_string_path (acc, path, option ? "true" : nullptr);
2573}
2574
2575static gboolean
2576get_kvp_boolean_path (const Account *acc, const Path& path)
2577{
2578 auto slot{QOF_INSTANCE(acc)->kvp_data->get_slot(path)};
2579 if (!slot) return false;
2580 switch (slot->get_type())
2581 {
2583 return slot->get<int64_t>() != 0;
2585 return g_strcmp0 (slot->get<const char*>(), "true") == 0;
2586 default:
2587 return false;
2588 }
2589}
2590
2591static void
2592set_kvp_int64_path (Account *acc, const Path& path, std::optional<gint64> value)
2593{
2595 qof_instance_set_path_kvp<int64_t> (QOF_INSTANCE(acc), value, path);
2597}
2598
2599static const std::optional<gint64>
2600get_kvp_int64_path (const Account *acc, const Path& path)
2601{
2602 return qof_instance_get_path_kvp<int64_t> (QOF_INSTANCE(acc), path);
2603}
2604
2605static GncGUID*
2606get_kvp_guid_path (const Account *acc, const Path& path)
2607{
2608 auto val{qof_instance_get_path_kvp<GncGUID*> (QOF_INSTANCE(acc), path)};
2609 return val ? guid_copy(*val) : nullptr;
2610}
2611
2612void
2613xaccAccountSetColor (Account *acc, const char *str)
2614{
2615 set_kvp_string_path (acc, {"color"}, str);
2616}
2617
2618void
2619xaccAccountSetFilter (Account *acc, const char *str)
2620{
2621 set_kvp_string_path (acc, {"filter"}, str);
2622}
2623
2624void
2625xaccAccountSetSortOrder (Account *acc, const char *str)
2626{
2627 set_kvp_string_path (acc, {"sort-order"}, str);
2628}
2629
2630void
2631xaccAccountSetSortReversed (Account *acc, gboolean sortreversed)
2632{
2633 set_kvp_boolean_path (acc, {"sort-reversed"}, sortreversed);
2634}
2635
2636static void
2637qofAccountSetParent (Account *acc, QofInstance *parent)
2638{
2639 Account *parent_acc;
2640
2641 g_return_if_fail(GNC_IS_ACCOUNT(acc));
2642 g_return_if_fail(GNC_IS_ACCOUNT(parent));
2643
2644 parent_acc = GNC_ACCOUNT(parent);
2646 xaccAccountBeginEdit(parent_acc);
2647 gnc_account_append_child(parent_acc, acc);
2648 mark_account (parent_acc);
2649 mark_account (acc);
2651 xaccAccountCommitEdit(parent_acc);
2652}
2653
2654void
2655xaccAccountSetNotes (Account *acc, const char *str)
2656{
2657 set_kvp_string_path (acc, {"notes"}, str);
2658}
2659
2660void
2661xaccAccountSetOnlineID (Account *acc, const char *id)
2662{
2663 g_return_if_fail (GNC_IS_ACCOUNT(acc));
2664 set_kvp_string_path (acc, {KEY_ONLINE_ID}, id);
2665}
2666
2667
2668void
2669xaccAccountSetAssociatedAccount (Account *acc, const char *tag, const Account* assoc_acct)
2670{
2671 g_return_if_fail (GNC_IS_ACCOUNT(acc));
2672 g_return_if_fail (tag && *tag);
2673
2674 set_kvp_account_path (acc, {"associated-account", tag}, assoc_acct);
2675}
2676
2677void
2678xaccAccountSetCommodity (Account * acc, gnc_commodity * com)
2679{
2680 AccountPrivate *priv;
2681
2682 /* errors */
2683 g_return_if_fail(GNC_IS_ACCOUNT(acc));
2684 g_return_if_fail(GNC_IS_COMMODITY(com));
2685
2686 /* optimizations */
2687 priv = GET_PRIVATE(acc);
2688 if (com == priv->commodity)
2689 return;
2690
2692 gnc_commodity_decrement_usage_count(priv->commodity);
2693 priv->commodity = com;
2695 priv->commodity_scu = gnc_commodity_get_fraction(com);
2696 priv->non_standard_scu = FALSE;
2697
2698 /* iterate over splits */
2699 for (auto s : priv->splits)
2700 {
2701 Transaction *trans = xaccSplitGetParent (s);
2702
2703 xaccTransBeginEdit (trans);
2705 xaccTransCommitEdit (trans);
2706 }
2707
2708 priv->sort_dirty = TRUE; /* Not needed. */
2709 priv->balance_dirty = TRUE;
2710 mark_account (acc);
2711
2713}
2714
2715/*
2716 * Set the account scu and then check to see if it is the same as the
2717 * commodity scu. This function is called when parsing the data file
2718 * and is designed to catch cases where the two were accidentally set
2719 * to mismatched values in the past.
2720 */
2721void
2723{
2724 AccountPrivate *priv;
2725
2726 g_return_if_fail(GNC_IS_ACCOUNT(acc));
2727
2728 priv = GET_PRIVATE(acc);
2730 priv->commodity_scu = scu;
2731 if (scu != gnc_commodity_get_fraction(priv->commodity))
2732 priv->non_standard_scu = TRUE;
2733 mark_account(acc);
2735}
2736
2737int
2739{
2740 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), 0);
2741 return GET_PRIVATE(acc)->commodity_scu;
2742}
2743
2744int
2746{
2747 AccountPrivate *priv;
2748
2749 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), 0);
2750
2751 priv = GET_PRIVATE(acc);
2752 if (priv->non_standard_scu || !priv->commodity)
2753 return priv->commodity_scu;
2754 return gnc_commodity_get_fraction(priv->commodity);
2755}
2756
2757void
2759{
2760 AccountPrivate *priv;
2761
2762 g_return_if_fail(GNC_IS_ACCOUNT(acc));
2763
2764 priv = GET_PRIVATE(acc);
2765 if (priv->non_standard_scu == flag)
2766 return;
2768 priv->non_standard_scu = flag;
2769 mark_account (acc);
2771}
2772
2773gboolean
2775{
2776 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), 0);
2777 return GET_PRIVATE(acc)->non_standard_scu;
2778}
2779
2780/********************************************************************\
2781\********************************************************************/
2782/* below follow the old, deprecated currency/security routines. */
2783
2784void
2785DxaccAccountSetCurrency (Account * acc, gnc_commodity * currency)
2786{
2787 if ((!acc) || (!currency)) return;
2788
2789 auto s = gnc_commodity_get_unique_name (currency);
2790 set_kvp_string_path (acc, {"old-currency"}, s);
2791
2792 auto book = qof_instance_get_book(acc);
2794 auto commodity = gnc_commodity_table_lookup_unique (table, s);
2795
2796 if (!commodity)
2798}
2799
2800/********************************************************************\
2801\********************************************************************/
2802
2803void
2804gnc_account_foreach_descendant (const Account *acc, std::function<void(Account*)> account_cb)
2805{
2806 g_return_if_fail (GNC_IS_ACCOUNT(acc));
2807
2808 // children must be a vector copy instead of reference because
2809 // some callers e.g. xaccAccountTreeScrubLots will modify the
2810 // children
2811 auto children = GET_PRIVATE(acc)->children;
2812 for (auto child : children)
2813 {
2814 account_cb (child);
2815 gnc_account_foreach_descendant (child, account_cb);
2816 }
2817}
2818
2819static void
2820account_foreach_descendant_sorted (const Account *acc, std::function<void(Account*)> account_cb)
2821{
2822 g_return_if_fail (GNC_IS_ACCOUNT(acc));
2823
2824 auto children = GET_PRIVATE(acc)->children;
2825 std::sort (children.begin(), children.end(),
2826 [](auto a, auto b) { return xaccAccountOrder (a, b) < 0; });
2827
2828 for (auto child : children)
2829 {
2830 account_cb (child);
2831 account_foreach_descendant_sorted (child, account_cb);
2832 }
2833}
2834
2835void
2837{
2838 AccountPrivate *ppriv, *cpriv;
2839 Account *old_parent;
2840 QofCollection *col;
2841
2842 /* errors */
2843 g_assert(GNC_IS_ACCOUNT(new_parent));
2844 g_assert(GNC_IS_ACCOUNT(child));
2845
2846 /* optimizations */
2847 ppriv = GET_PRIVATE(new_parent);
2848 cpriv = GET_PRIVATE(child);
2849 old_parent = cpriv->parent;
2850 if (old_parent == new_parent)
2851 return;
2852
2853 // xaccAccountBeginEdit(new_parent);
2854 xaccAccountBeginEdit(child);
2855 if (old_parent)
2856 {
2857 gnc_account_remove_child(old_parent, child);
2858
2859 if (!qof_instance_books_equal(old_parent, new_parent))
2860 {
2861 /* hack alert -- this implementation is not exactly correct.
2862 * If the entity tables are not identical, then the 'from' book
2863 * may have a different backend than the 'to' book. This means
2864 * that we should get the 'from' backend to destroy this account,
2865 * and the 'to' backend to save it. Right now, this is broken.
2866 *
2867 * A 'correct' implementation similar to this is in Period.c
2868 * except its for transactions ...
2869 *
2870 * Note also, we need to reparent the children to the new book as well.
2871 */
2872 PWARN ("reparenting accounts across books is not correctly supported\n");
2873
2874 qof_event_gen (&child->inst, QOF_EVENT_DESTROY, nullptr);
2876 GNC_ID_ACCOUNT);
2877 qof_collection_insert_entity (col, &child->inst);
2878 qof_event_gen (&child->inst, QOF_EVENT_CREATE, nullptr);
2879 }
2880 }
2881 cpriv->parent = new_parent;
2882 ppriv->children.push_back (child);
2883 qof_instance_set_dirty(&new_parent->inst);
2884 qof_instance_set_dirty(&child->inst);
2885
2886 /* Send events data. Warning: The call to commit_edit is also going
2887 * to send a MODIFY event. If the gtktreemodelfilter code gets the
2888 * MODIFY before it gets the ADD, it gets very confused and thinks
2889 * that two nodes have been added. */
2890 qof_event_gen (&child->inst, QOF_EVENT_ADD, nullptr);
2891 // qof_event_gen (&new_parent->inst, QOF_EVENT_MODIFY, nullptr);
2892
2893 xaccAccountCommitEdit (child);
2894 // xaccAccountCommitEdit(new_parent);
2895}
2896
2897void
2899{
2900 AccountPrivate *ppriv, *cpriv;
2901 GncEventData ed;
2902
2903 if (!child) return;
2904
2905 /* Note this routine might be called on accounts which
2906 * are not yet parented. */
2907 if (!parent) return;
2908
2909 ppriv = GET_PRIVATE(parent);
2910 cpriv = GET_PRIVATE(child);
2911
2912 if (cpriv->parent != parent)
2913 {
2914 PERR ("account not a child of parent");
2915 return;
2916 }
2917
2918 /* Gather event data */
2919 ed.node = parent;
2920 ed.idx = gnc_account_child_index (parent, child);
2921
2922 ppriv->children.erase (std::remove (ppriv->children.begin(), ppriv->children.end(), child),
2923 ppriv->children.end());
2924
2925 /* Now send the event. */
2926 qof_event_gen(&child->inst, QOF_EVENT_REMOVE, &ed);
2927
2928 /* clear the account's parent pointer after REMOVE event generation. */
2929 cpriv->parent = nullptr;
2930
2931 qof_event_gen (&parent->inst, QOF_EVENT_MODIFY, nullptr);
2932}
2933
2934Account *
2936{
2937 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
2938 return GET_PRIVATE(acc)->parent;
2939}
2940
2941Account *
2943{
2944 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
2945
2946 while (auto parent = gnc_account_get_parent (acc))
2947 acc = parent;
2948
2949 return acc;
2950}
2951
2952gboolean
2954{
2955 g_return_val_if_fail(GNC_IS_ACCOUNT(account), FALSE);
2956 return (GET_PRIVATE(account)->parent == nullptr);
2957}
2958
2959GList *
2961{
2962 g_return_val_if_fail(GNC_IS_ACCOUNT(account), nullptr);
2963 auto& children = GET_PRIVATE(account)->children;
2964 return std::accumulate (children.rbegin(), children.rend(), static_cast<GList*>(nullptr),
2965 g_list_prepend);
2966}
2967
2968GList *
2970{
2971 g_return_val_if_fail(GNC_IS_ACCOUNT(account), nullptr);
2972 return g_list_sort(gnc_account_get_children (account), (GCompareFunc)xaccAccountOrder);
2973}
2974
2975gint
2977{
2978 g_return_val_if_fail(GNC_IS_ACCOUNT(account), 0);
2979 return GET_PRIVATE(account)->children.size();
2980}
2981
2982gint
2983gnc_account_child_index (const Account *parent, const Account *child)
2984{
2985 g_return_val_if_fail(GNC_IS_ACCOUNT(parent), -1);
2986 g_return_val_if_fail(GNC_IS_ACCOUNT(child), -1);
2987 auto& children = GET_PRIVATE(parent)->children;
2988 auto find_it = std::find (children.begin(), children.end(), child);
2989 return find_it == children.end() ? -1 : std::distance (children.begin(), find_it);
2990}
2991
2992Account *
2993gnc_account_nth_child (const Account *parent, gint num)
2994{
2995 g_return_val_if_fail(GNC_IS_ACCOUNT(parent), nullptr);
2996 if ((size_t)num >= GET_PRIVATE(parent)->children.size())
2997 return nullptr;
2998 return static_cast<Account*>(GET_PRIVATE(parent)->children.at (num));
2999}
3000
3001gint
3003{
3004 int count {0};
3005 gnc_account_foreach_descendant (account, [&count](auto acc){ count++; });
3006 return count;
3007}
3008
3009gint
3011{
3012 AccountPrivate *priv;
3013 int depth = 0;
3014
3015 g_return_val_if_fail(GNC_IS_ACCOUNT(account), 0);
3016
3017 priv = GET_PRIVATE(account);
3018 while (priv->parent && (priv->type != ACCT_TYPE_ROOT))
3019 {
3020 account = priv->parent;
3021 priv = GET_PRIVATE(account);
3022 depth++;
3023 }
3024
3025 return depth;
3026}
3027
3028gint
3030{
3031 AccountPrivate *priv;
3032 g_return_val_if_fail(GNC_IS_ACCOUNT(account), 0);
3033
3034 priv = GET_PRIVATE(account);
3035 if (!priv->children.size())
3036 return 1;
3037
3038 return 1 + std::accumulate (priv->children.begin(), priv->children.end(),
3039 0, [](auto a, auto b)
3040 { return std::max (a, gnc_account_get_tree_depth (b)); });
3041}
3042
3043GList *
3045{
3046 GList* list = nullptr;
3047 gnc_account_foreach_descendant (account, [&list](auto a){ list = g_list_prepend (list, a); });
3048 return g_list_reverse (list);
3049}
3050
3051GList *
3053{
3054 GList* list = nullptr;
3055 account_foreach_descendant_sorted (account, [&list](auto a){ list = g_list_prepend (list, a); });
3056 return g_list_reverse (list);
3057}
3058
3059// because gnc_account_lookup_by_name and gnc_account_lookup_by_code
3060// are described in Account.h searching breadth-first until 4.6, and
3061// accidentally modified to search depth-first from 4.7
3062// onwards. Restore breath-first searching in 4.11 onwards to match
3063// previous behaviour and function description in Account.h
3064static gpointer
3065account_foreach_descendant_breadthfirst_until (const Account *acc,
3066 AccountCb2 thunk,
3067 gpointer user_data)
3068{
3069 g_return_val_if_fail (GNC_IS_ACCOUNT(acc), nullptr);
3070 g_return_val_if_fail (thunk, nullptr);
3071
3072 auto& children{GET_PRIVATE(acc)->children};
3073
3074 for (auto acc : children)
3075 if (auto result = thunk (acc, user_data))
3076 return result;
3077
3078 for (auto acc: children)
3079 if (auto result = account_foreach_descendant_breadthfirst_until (acc, thunk, user_data))
3080 return result;
3081
3082 return nullptr;
3083}
3084
3085static gpointer
3086is_acct_name (Account *account, gpointer user_data)
3087{
3088 auto name {static_cast<gchar*>(user_data)};
3089 return (g_strcmp0 (name, xaccAccountGetName (account)) ? nullptr : account);
3090}
3091
3092Account *
3093gnc_account_lookup_by_name (const Account *parent, const char * name)
3094{
3095 return (Account*)account_foreach_descendant_breadthfirst_until (parent, is_acct_name, (char*)name);
3096}
3097
3098static gpointer
3099is_acct_code (Account *account, gpointer user_data)
3100{
3101 auto name {static_cast<gchar*>(user_data)};
3102 return (g_strcmp0 (name, xaccAccountGetCode (account)) ? nullptr : account);
3103}
3104
3105Account *
3106gnc_account_lookup_by_code (const Account *parent, const char * code)
3107{
3108 return (Account*)account_foreach_descendant_breadthfirst_until (parent, is_acct_code, (char*)code);
3109}
3110
3111static gpointer
3112is_opening_balance_account (Account* account, gpointer data)
3113{
3114 gnc_commodity* commodity = GNC_COMMODITY(data);
3116 return account;
3117 return nullptr;
3118}
3119
3120Account*
3121gnc_account_lookup_by_opening_balance (Account* account, gnc_commodity* commodity)
3122{
3123 return (Account *)gnc_account_foreach_descendant_until (account, is_opening_balance_account, commodity);
3124}
3125
3126/********************************************************************\
3127 * Fetch an account, given its full name *
3128\********************************************************************/
3129
3130static Account *
3131gnc_account_lookup_by_full_name_helper (const Account *parent,
3132 gchar **names)
3133{
3134 g_return_val_if_fail(GNC_IS_ACCOUNT(parent), nullptr);
3135 g_return_val_if_fail(names, nullptr);
3136
3137 /* Look for the first name in the children. */
3138 for (auto account : GET_PRIVATE(parent)->children)
3139 {
3140 auto priv = GET_PRIVATE(account);
3141 if (g_strcmp0(priv->accountName, names[0]) == 0)
3142 {
3143 /* We found an account. If the next entry is nullptr, there is
3144 * nothing left in the name, so just return the account. */
3145 if (names[1] == nullptr)
3146 return account;
3147
3148 /* No children? We're done. */
3149 if (priv->children.empty())
3150 return nullptr;
3151
3152 /* There's stuff left to search for. Search recursively. */
3153 if (auto found = gnc_account_lookup_by_full_name_helper(account, &names[1]))
3154 return found;
3155 }
3156 }
3157
3158 return nullptr;
3159}
3160
3161
3162Account *
3164 const gchar *name)
3165{
3166 const AccountPrivate *rpriv;
3167 const Account *root;
3168 Account *found;
3169 gchar **names;
3170
3171 g_return_val_if_fail(GNC_IS_ACCOUNT(any_acc), nullptr);
3172 g_return_val_if_fail(name, nullptr);
3173
3174 root = any_acc;
3175 rpriv = GET_PRIVATE(root);
3176 while (rpriv->parent)
3177 {
3178 root = rpriv->parent;
3179 rpriv = GET_PRIVATE(root);
3180 }
3181 names = g_strsplit(name, gnc_get_account_separator_string(), -1);
3182 found = gnc_account_lookup_by_full_name_helper(root, names);
3183 g_strfreev(names);
3184 return found;
3185}
3186
3187GList*
3189 const char* name,
3190 GNCAccountType acctype,
3191 gnc_commodity* commodity)
3192{
3193 GList *retval{};
3194 auto rpriv{GET_PRIVATE(root)};
3195 for (auto account : rpriv->children)
3196 {
3197 if (xaccAccountGetType (account) == acctype)
3198 {
3199 if (commodity &&
3201 commodity))
3202 continue;
3203
3204 if (name && strcmp(name, xaccAccountGetName(account)))
3205 continue;
3206
3207 retval = g_list_prepend(retval, account);
3208 }
3209 }
3210
3211 if (!retval) // Recurse through the children
3212 for (auto account : rpriv->children)
3213 {
3214 auto result = gnc_account_lookup_by_type_and_commodity(account,
3215 name,
3216 acctype,
3217 commodity);
3218 if (result)
3219 retval = g_list_concat(result, retval);
3220 }
3221 return retval;
3222}
3223
3224void
3226 AccountCb thunk,
3227 gpointer user_data)
3228{
3229 g_return_if_fail(GNC_IS_ACCOUNT(acc));
3230 g_return_if_fail(thunk);
3231 std::for_each (GET_PRIVATE(acc)->children.begin(), GET_PRIVATE(acc)->children.end(),
3232 [user_data, thunk](auto a){ thunk (a, user_data); });
3233}
3234
3235void
3236gnc_account_foreach_descendant (const Account *acc,
3237 AccountCb thunk,
3238 gpointer user_data)
3239{
3240 gnc_account_foreach_descendant (acc, [&](auto acc){ thunk (acc, user_data); });
3241}
3242
3243gpointer
3245 AccountCb2 thunk,
3246 gpointer user_data)
3247{
3248 gpointer result {nullptr};
3249
3250 g_return_val_if_fail (GNC_IS_ACCOUNT(acc), nullptr);
3251 g_return_val_if_fail (thunk, nullptr);
3252
3253 for (auto child : GET_PRIVATE(acc)->children)
3254 {
3255 result = thunk (child, user_data);
3256 if (result) break;
3257
3258 result = gnc_account_foreach_descendant_until (child, thunk, user_data);
3259 if (result) break;
3260 }
3261
3262 return result;
3263}
3264
3265
3268{
3269 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), ACCT_TYPE_NONE);
3270 return GET_PRIVATE(acc)->type;
3271}
3272
3273static const char*
3274qofAccountGetTypeString (const Account *acc)
3275{
3276 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
3277 return xaccAccountTypeEnumAsString(GET_PRIVATE(acc)->type);
3278}
3279
3280static void
3281qofAccountSetType (Account *acc, const char *type_string)
3282{
3283 g_return_if_fail(GNC_IS_ACCOUNT(acc));
3284 g_return_if_fail(type_string);
3286}
3287
3288const char *
3290{
3291 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
3292 return GET_PRIVATE(acc)->accountName;
3293}
3294
3295std::vector<const Account*>
3296gnc_account_get_all_parents (const Account *account)
3297{
3298 std::vector<const Account*> rv;
3299 for (auto a = account; !gnc_account_is_root (a); a = gnc_account_get_parent (a))
3300 rv.push_back (a);
3301 return rv;
3302}
3303
3304gchar *
3306{
3307 /* So much for hardening the API. Too many callers to this function don't
3308 * bother to check if they have a non-nullptr pointer before calling. */
3309 if (nullptr == account)
3310 return g_strdup("");
3311
3312 /* errors */
3313 g_return_val_if_fail(GNC_IS_ACCOUNT(account), g_strdup(""));
3314
3315 auto path{gnc_account_get_all_parents (account)};
3316 auto seps_size{path.empty() ? 0 : strlen (account_separator) * (path.size() - 1)};
3317 auto alloc_size{std::accumulate (path.begin(), path.end(), seps_size,
3318 [](auto sum, auto acc)
3319 { return sum + strlen (xaccAccountGetName (acc)); })};
3320 auto rv = g_new (char, alloc_size + 1);
3321 auto p = rv;
3322
3323 std::for_each (path.rbegin(), path.rend(),
3324 [&p, rv](auto a)
3325 {
3326 if (p != rv)
3327 p = stpcpy (p, account_separator);
3328 p = stpcpy (p, xaccAccountGetName (a));
3329 });
3330 *p = '\0';
3331
3332 return rv;
3333}
3334
3335const char *
3337{
3338 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
3339 return GET_PRIVATE(acc)->accountCode;
3340}
3341
3342const char *
3344{
3345 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
3346 return GET_PRIVATE(acc)->description;
3347}
3348
3349const char *
3351{
3352 return get_kvp_string_path (acc, {"color"});
3353}
3354
3355const char *
3357{
3358 return get_kvp_string_path (acc, {"filter"});
3359}
3360
3361const char *
3363{
3364 return get_kvp_string_path (acc, {"sort-order"});
3365}
3366
3367gboolean
3369{
3370 return get_kvp_boolean_path (acc, {"sort-reversed"});
3371}
3372
3373const char *
3375{
3376 return get_kvp_string_path (acc, {"notes"});
3377}
3378
3379const char *
3381{
3382 g_return_val_if_fail (GNC_IS_ACCOUNT(acc), nullptr);
3383 return get_kvp_string_path (acc, {KEY_ONLINE_ID});
3384}
3385
3386Account*
3387xaccAccountGetAssociatedAccount (const Account *acc, const char *tag)
3388{
3389 g_return_val_if_fail (tag && *tag, nullptr);
3390
3391 return get_kvp_account_path (acc, {"associated-account", tag});
3392}
3393
3394
3395gnc_commodity *
3397{
3398 if (auto s = get_kvp_string_path (acc, {"old-currency"}))
3399 {
3401 return gnc_commodity_table_lookup_unique (table, s);
3402 }
3403
3404 return nullptr;
3405}
3406
3407gnc_commodity *
3409{
3410 if (!GNC_IS_ACCOUNT(acc))
3411 return nullptr;
3412 return GET_PRIVATE(acc)->commodity;
3413}
3414
3415gnc_commodity * gnc_account_get_currency_or_parent(const Account* account)
3416{
3417 g_return_val_if_fail (GNC_IS_ACCOUNT (account), nullptr);
3418
3419 for (auto acc = account; acc; acc = gnc_account_get_parent (acc))
3420 if (auto comm = xaccAccountGetCommodity (acc); gnc_commodity_is_currency (comm))
3421 return comm;
3422
3423 return nullptr; // no suitable commodity found.
3424}
3425
3426/********************************************************************\
3427\********************************************************************/
3428void
3429gnc_account_set_start_balance (Account *acc, const gnc_numeric start_baln)
3430{
3431 AccountPrivate *priv;
3432
3433 g_return_if_fail(GNC_IS_ACCOUNT(acc));
3434
3435 priv = GET_PRIVATE(acc);
3436 priv->starting_balance = start_baln;
3437 priv->balance_dirty = TRUE;
3438}
3439
3440void
3442 const gnc_numeric start_baln)
3443{
3444 AccountPrivate *priv;
3445
3446 g_return_if_fail(GNC_IS_ACCOUNT(acc));
3447
3448 priv = GET_PRIVATE(acc);
3449 priv->starting_cleared_balance = start_baln;
3450 priv->balance_dirty = TRUE;
3451}
3452
3453void
3455 const gnc_numeric start_baln)
3456{
3457 AccountPrivate *priv;
3458
3459 g_return_if_fail(GNC_IS_ACCOUNT(acc));
3460
3461 priv = GET_PRIVATE(acc);
3462 priv->starting_reconciled_balance = start_baln;
3463 priv->balance_dirty = TRUE;
3464}
3465
3466gnc_numeric
3468{
3469 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
3470 return GET_PRIVATE(acc)->balance;
3471}
3472
3473gnc_numeric
3475{
3476 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
3477 return GET_PRIVATE(acc)->cleared_balance;
3478}
3479
3480gnc_numeric
3482{
3483 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
3484 return GET_PRIVATE(acc)->reconciled_balance;
3485}
3486
3487gnc_numeric
3488xaccAccountGetProjectedMinimumBalance (const Account *acc)
3489{
3490 auto today{gnc_time64_get_today_end()};
3491 std::optional<gnc_numeric> minimum;
3492
3493 auto before_today_end = [&minimum, today](const Split *s) -> bool
3494 {
3495 auto bal{xaccSplitGetBalance(s)};
3496 if (!minimum || gnc_numeric_compare (bal, *minimum) < 0)
3497 minimum = bal;
3498 return (xaccTransGetDate(xaccSplitGetParent(s)) < today);
3499 };
3500 // scan to find today's split, but we're really interested in the
3501 // minimum balance
3502 [[maybe_unused]] auto todays_split = gnc_account_find_split (acc, before_today_end, true);
3503 return minimum ? *minimum : gnc_numeric_zero();
3504}
3505
3506gboolean
3508{
3509 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), false);
3510 return GET_PRIVATE(acc)->has_stock_split;
3511}
3512
3513
3514/********************************************************************\
3515\********************************************************************/
3516
3517static gnc_numeric
3518GetBalanceAsOfDate (Account *acc, time64 date, std::function<gnc_numeric(Split*)> split_to_numeric)
3519{
3520 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
3521
3522 xaccAccountSortSplits (acc, TRUE); /* just in case, normally a noop */
3523 xaccAccountRecomputeBalance (acc); /* just in case, normally a noop */
3524
3525 auto is_before_date = [date](auto s) -> bool
3526 { return xaccTransGetDate(xaccSplitGetParent(s)) < date; };
3527
3528 auto latest_split{gnc_account_find_split (acc, is_before_date, true)};
3529 return latest_split ? split_to_numeric (latest_split) : gnc_numeric_zero();
3530}
3531
3532gnc_numeric
3534{
3535 return GetBalanceAsOfDate (acc, date, xaccSplitGetBalance);
3536}
3537
3538static gnc_numeric
3539xaccAccountGetNoclosingBalanceAsOfDate (Account *acc, time64 date)
3540{
3541 return GetBalanceAsOfDate (acc, date, xaccSplitGetNoclosingBalance);
3542}
3543
3544gnc_numeric
3546{
3547 return GetBalanceAsOfDate (acc, date, xaccSplitGetReconciledBalance);
3548}
3549
3550/*
3551 * Originally gsr_account_present_balance in gnc-split-reg.c
3552 */
3553gnc_numeric
3554xaccAccountGetPresentBalance (const Account *acc)
3555{
3556 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
3557
3558 return xaccAccountGetBalanceAsOfDate (GNC_ACCOUNT (acc),
3560}
3561
3562
3563/********************************************************************\
3564\********************************************************************/
3565/* XXX TODO: These 'GetBal' routines should be moved to some
3566 * utility area outside of the core account engine area.
3567 */
3568
3569/*
3570 * Convert a balance from one currency to another.
3571 */
3572gnc_numeric
3573xaccAccountConvertBalanceToCurrency(const Account *acc, /* for book */
3574 gnc_numeric balance,
3575 const gnc_commodity *balance_currency,
3576 const gnc_commodity *new_currency)
3577{
3578 QofBook *book;
3579 GNCPriceDB *pdb;
3580
3581 if (gnc_numeric_zero_p (balance) ||
3582 gnc_commodity_equiv (balance_currency, new_currency))
3583 return balance;
3584
3585 book = gnc_account_get_book (acc);
3586 pdb = gnc_pricedb_get_db (book);
3587
3589 pdb, balance, balance_currency, new_currency);
3590
3591 return balance;
3592}
3593
3594/*
3595 * Convert a balance from one currency to another with price of
3596 * a given date.
3597 */
3598gnc_numeric
3599xaccAccountConvertBalanceToCurrencyAsOfDate(const Account *acc, /* for book */
3600 gnc_numeric balance,
3601 const gnc_commodity *balance_currency,
3602 const gnc_commodity *new_currency,
3603 time64 date)
3604{
3605 QofBook *book;
3606 GNCPriceDB *pdb;
3607
3608 if (gnc_numeric_zero_p (balance) ||
3609 gnc_commodity_equiv (balance_currency, new_currency))
3610 return balance;
3611
3612 book = gnc_account_get_book (acc);
3613 pdb = gnc_pricedb_get_db (book);
3614
3616 pdb, balance, balance_currency, new_currency, date);
3617
3618 return balance;
3619}
3620
3621/*
3622 * Given an account and a GetBalanceFn pointer, extract the requested
3623 * balance from the account and then convert it to the desired
3624 * currency.
3625 */
3626static gnc_numeric
3627xaccAccountGetXxxBalanceInCurrency (const Account *acc,
3628 xaccGetBalanceFn fn,
3629 const gnc_commodity *report_currency)
3630{
3631 AccountPrivate *priv;
3632 gnc_numeric balance;
3633
3634 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
3635 g_return_val_if_fail(fn, gnc_numeric_zero());
3636 g_return_val_if_fail(GNC_IS_COMMODITY(report_currency), gnc_numeric_zero());
3637
3638 priv = GET_PRIVATE(acc);
3639 balance = fn(acc);
3640 balance = xaccAccountConvertBalanceToCurrency(acc, balance,
3641 priv->commodity,
3642 report_currency);
3643 return balance;
3644}
3645
3646static gnc_numeric
3647xaccAccountGetXxxBalanceAsOfDateInCurrency(Account *acc, time64 date,
3648 xaccGetBalanceAsOfDateFn fn,
3649 const gnc_commodity *report_commodity)
3650{
3651 AccountPrivate *priv;
3652
3653 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
3654 g_return_val_if_fail(fn, gnc_numeric_zero());
3655 g_return_val_if_fail(GNC_IS_COMMODITY(report_commodity), gnc_numeric_zero());
3656
3657 priv = GET_PRIVATE(acc);
3658 return xaccAccountConvertBalanceToCurrencyAsOfDate(
3659 acc, fn(acc, date), priv->commodity, report_commodity, date);
3660}
3661
3662/*
3663 * Data structure used to pass various arguments into the following fn.
3664 */
3665typedef struct
3666{
3667 const gnc_commodity *currency;
3668 gnc_numeric balance;
3669 xaccGetBalanceFn fn;
3670 xaccGetBalanceAsOfDateFn asOfDateFn;
3671 time64 date;
3673
3674
3675/*
3676 * A helper function for iterating over all the accounts in a list or
3677 * tree. This function is called once per account, and sums up the
3678 * values of all these accounts.
3679 */
3680static void
3681xaccAccountBalanceHelper (Account *acc, gpointer data)
3682{
3683 CurrencyBalance *cb = static_cast<CurrencyBalance*>(data);
3684 gnc_numeric balance;
3685
3686 if (!cb->fn || !cb->currency)
3687 return;
3688 balance = xaccAccountGetXxxBalanceInCurrency (acc, cb->fn, cb->currency);
3689 cb->balance = gnc_numeric_add (cb->balance, balance,
3690 gnc_commodity_get_fraction (cb->currency),
3691 GNC_HOW_RND_ROUND_HALF_UP);
3692}
3693
3694static void
3695xaccAccountBalanceAsOfDateHelper (Account *acc, gpointer data)
3696{
3697 CurrencyBalance *cb = static_cast<CurrencyBalance*>(data);
3698 gnc_numeric balance;
3699
3700 g_return_if_fail (cb->asOfDateFn && cb->currency);
3701
3702 balance = xaccAccountGetXxxBalanceAsOfDateInCurrency (
3703 acc, cb->date, cb->asOfDateFn, cb->currency);
3704 cb->balance = gnc_numeric_add (cb->balance, balance,
3705 gnc_commodity_get_fraction (cb->currency),
3706 GNC_HOW_RND_ROUND_HALF_UP);
3707}
3708
3709
3710
3711/*
3712 * Common function that iterates recursively over all accounts below
3713 * the specified account. It uses xaccAccountBalanceHelper to sum up
3714 * the balances of all its children, and uses the specified function
3715 * 'fn' for extracting the balance. This function may extract the
3716 * current value, the reconciled value, etc.
3717 *
3718 * If 'report_commodity' is nullptr, just use the account's commodity.
3719 * If 'include_children' is FALSE, this function doesn't recurse at all.
3720 */
3721static gnc_numeric
3722xaccAccountGetXxxBalanceInCurrencyRecursive (const Account *acc,
3723 xaccGetBalanceFn fn,
3724 const gnc_commodity *report_commodity,
3725 gboolean include_children)
3726{
3727 gnc_numeric balance;
3728
3729 if (!acc) return gnc_numeric_zero ();
3730 if (!report_commodity)
3731 report_commodity = xaccAccountGetCommodity (acc);
3732 if (!report_commodity)
3733 return gnc_numeric_zero();
3734
3735 balance = xaccAccountGetXxxBalanceInCurrency (acc, fn, report_commodity);
3736
3737 /* If needed, sum up the children converting to the *requested*
3738 commodity. */
3739 if (include_children)
3740 {
3741#ifdef _MSC_VER
3742 /* MSVC compiler: Somehow, the struct initialization containing a
3743 gnc_numeric doesn't work. As an exception, we hand-initialize
3744 that member afterwards. */
3745 CurrencyBalance cb = { report_commodity, { 0 }, fn, nullptr, 0 };
3746 cb.balance = balance;
3747#else
3748 CurrencyBalance cb = { report_commodity, balance, fn, nullptr, 0 };
3749#endif
3750
3751 gnc_account_foreach_descendant (acc, xaccAccountBalanceHelper, &cb);
3752 balance = cb.balance;
3753 }
3754
3755 return balance;
3756}
3757
3758static gnc_numeric
3759xaccAccountGetXxxBalanceAsOfDateInCurrencyRecursive (
3760 Account *acc, time64 date, xaccGetBalanceAsOfDateFn fn,
3761 const gnc_commodity *report_commodity, gboolean include_children)
3762{
3763 gnc_numeric balance;
3764
3765 g_return_val_if_fail(acc, gnc_numeric_zero());
3766 if (!report_commodity)
3767 report_commodity = xaccAccountGetCommodity (acc);
3768 if (!report_commodity)
3769 return gnc_numeric_zero();
3770
3771 balance = xaccAccountGetXxxBalanceAsOfDateInCurrency(
3772 acc, date, fn, report_commodity);
3773
3774 /* If needed, sum up the children converting to the *requested*
3775 commodity. */
3776 if (include_children)
3777 {
3778#ifdef _MSC_VER
3779 /* MSVC compiler: Somehow, the struct initialization containing a
3780 gnc_numeric doesn't work. As an exception, we hand-initialize
3781 that member afterwards. */
3782 CurrencyBalance cb = { report_commodity, 0, nullptr, fn, date };
3783 cb.balance = balance;
3784#else
3785 CurrencyBalance cb = { report_commodity, balance, nullptr, fn, date };
3786#endif
3787
3788 gnc_account_foreach_descendant (acc, xaccAccountBalanceAsOfDateHelper, &cb);
3789 balance = cb.balance;
3790 }
3791
3792 return balance;
3793}
3794
3795gnc_numeric
3796xaccAccountGetBalanceInCurrency (const Account *acc,
3797 const gnc_commodity *report_commodity,
3798 gboolean include_children)
3799{
3800 gnc_numeric rc;
3801 rc = xaccAccountGetXxxBalanceInCurrencyRecursive (
3802 acc, xaccAccountGetBalance, report_commodity, include_children);
3803 PINFO(" baln=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, rc.num, rc.denom);
3804 return rc;
3805}
3806
3807
3808gnc_numeric
3809xaccAccountGetClearedBalanceInCurrency (const Account *acc,
3810 const gnc_commodity *report_commodity,
3811 gboolean include_children)
3812{
3813 return xaccAccountGetXxxBalanceInCurrencyRecursive (
3814 acc, xaccAccountGetClearedBalance, report_commodity,
3815 include_children);
3816}
3817
3818gnc_numeric
3819xaccAccountGetReconciledBalanceInCurrency (const Account *acc,
3820 const gnc_commodity *report_commodity,
3821 gboolean include_children)
3822{
3823 return xaccAccountGetXxxBalanceInCurrencyRecursive (
3824 acc, xaccAccountGetReconciledBalance, report_commodity,
3825 include_children);
3826}
3827
3828gnc_numeric
3829xaccAccountGetPresentBalanceInCurrency (const Account *acc,
3830 const gnc_commodity *report_commodity,
3831 gboolean include_children)
3832{
3833 return xaccAccountGetXxxBalanceAsOfDateInCurrencyRecursive (
3835 report_commodity,
3836 include_children);
3837}
3838
3839gnc_numeric
3840xaccAccountGetProjectedMinimumBalanceInCurrency (
3841 const Account *acc,
3842 const gnc_commodity *report_commodity,
3843 gboolean include_children)
3844{
3845 return xaccAccountGetXxxBalanceInCurrencyRecursive (
3846 acc, xaccAccountGetProjectedMinimumBalance, report_commodity,
3847 include_children);
3848}
3849
3850gnc_numeric
3852 Account *acc, time64 date, gnc_commodity *report_commodity,
3853 gboolean include_children)
3854{
3855 return xaccAccountGetXxxBalanceAsOfDateInCurrencyRecursive (
3856 acc, date, xaccAccountGetBalanceAsOfDate, report_commodity,
3857 include_children);
3858}
3859
3860gnc_numeric
3862 Account *acc, time64 date, gnc_commodity *report_commodity,
3863 gboolean include_children)
3864{
3865 return xaccAccountGetXxxBalanceAsOfDateInCurrencyRecursive
3866 (acc, date, xaccAccountGetNoclosingBalanceAsOfDate,
3867 report_commodity, include_children);
3868}
3869
3870gnc_numeric
3871xaccAccountGetBalanceChangeForPeriod (Account *acc, time64 t1, time64 t2,
3872 gboolean recurse)
3873{
3874 gnc_numeric b1, b2;
3875
3876 b1 = xaccAccountGetBalanceAsOfDateInCurrency(acc, t1, nullptr, recurse);
3877 b2 = xaccAccountGetBalanceAsOfDateInCurrency(acc, t2, nullptr, recurse);
3878 return gnc_numeric_sub(b2, b1, GNC_DENOM_AUTO, GNC_HOW_DENOM_FIXED);
3879}
3880
3881gnc_numeric
3882xaccAccountGetNoclosingBalanceChangeForPeriod (Account *acc, time64 t1,
3883 time64 t2, gboolean recurse)
3884{
3885 gnc_numeric b1, b2;
3886
3887 b1 = xaccAccountGetNoclosingBalanceAsOfDateInCurrency(acc, t1, nullptr, recurse);
3888 b2 = xaccAccountGetNoclosingBalanceAsOfDateInCurrency(acc, t2, nullptr, recurse);
3889 return gnc_numeric_sub(b2, b1, GNC_DENOM_AUTO, GNC_HOW_DENOM_FIXED);
3890}
3891
3892typedef struct
3893{
3894 const gnc_commodity *currency;
3895 gnc_numeric balanceChange;
3896 time64 t1;
3897 time64 t2;
3899
3900static void
3901xaccAccountBalanceChangeHelper (Account *acc, gpointer data)
3902{
3903 CurrencyBalanceChange *cbdiff = static_cast<CurrencyBalanceChange*>(data);
3904
3905 gnc_numeric b1, b2;
3906 b1 = GetBalanceAsOfDate(acc, cbdiff->t1, xaccSplitGetNoclosingBalance);
3907 b2 = GetBalanceAsOfDate(acc, cbdiff->t2, xaccSplitGetNoclosingBalance);
3908 gnc_numeric balanceChange = gnc_numeric_sub(b2, b1, GNC_DENOM_AUTO, GNC_HOW_DENOM_FIXED);
3909 gnc_numeric balanceChange_conv = xaccAccountConvertBalanceToCurrencyAsOfDate(acc, balanceChange, xaccAccountGetCommodity(acc), cbdiff->currency, cbdiff->t2);
3910 cbdiff->balanceChange = gnc_numeric_add (cbdiff->balanceChange, balanceChange_conv,
3911 gnc_commodity_get_fraction (cbdiff->currency),
3912 GNC_HOW_RND_ROUND_HALF_UP);
3913}
3914
3915gnc_numeric
3916xaccAccountGetNoclosingBalanceChangeInCurrencyForPeriod (Account *acc, time64 t1,
3917 time64 t2, gboolean recurse)
3918{
3919
3920
3921 gnc_numeric b1, b2;
3922 b1 = GetBalanceAsOfDate(acc, t1, xaccSplitGetNoclosingBalance);
3923 b2 = GetBalanceAsOfDate(acc, t2, xaccSplitGetNoclosingBalance);
3924 gnc_numeric balanceChange = gnc_numeric_sub(b2, b1, GNC_DENOM_AUTO, GNC_HOW_DENOM_FIXED);
3925
3926 gnc_commodity *report_commodity = xaccAccountGetCommodity(acc);
3927 CurrencyBalanceChange cbdiff = { report_commodity, balanceChange, t1, t2 };
3928
3929 if(recurse)
3930 {
3931 gnc_account_foreach_descendant (acc, xaccAccountBalanceChangeHelper, &cbdiff);
3932 balanceChange = cbdiff.balanceChange;
3933 }
3934 return balanceChange;
3935}
3936
3937/********************************************************************\
3938\********************************************************************/
3939
3940const SplitsVec&
3941xaccAccountGetSplits (const Account *account)
3942{
3943 static const SplitsVec empty;
3944 g_return_val_if_fail (GNC_IS_ACCOUNT(account), empty);
3945 return GET_PRIVATE(account)->splits;
3946}
3947
3948SplitList *
3950{
3951 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
3952 auto priv{GET_PRIVATE(acc)};
3953 return std::accumulate (priv->splits.rbegin(), priv->splits.rend(),
3954 static_cast<GList*>(nullptr), g_list_prepend);
3955}
3956
3957size_t
3958xaccAccountGetSplitsSize (const Account *account)
3959{
3960 g_return_val_if_fail (GNC_IS_ACCOUNT(account), 0);
3961 return GNC_IS_ACCOUNT(account) ? GET_PRIVATE(account)->splits.size() : 0;
3962}
3963
3964gboolean
3965gnc_account_and_descendants_empty (Account *acc)
3966{
3967 g_return_val_if_fail (GNC_IS_ACCOUNT (acc), FALSE);
3968 auto priv = GET_PRIVATE (acc);
3969 if (!priv->splits.empty()) return FALSE;
3970 return std::all_of (priv->children.begin(), priv->children.end(),
3971 gnc_account_and_descendants_empty);
3972}
3973
3974LotList *
3976{
3977 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
3978 return g_list_copy(GET_PRIVATE(acc)->lots);
3979}
3980
3981LotList *
3983 gboolean (*match_func)(GNCLot *lot,
3984 gpointer user_data),
3985 gpointer user_data, GCompareFunc sort_func)
3986{
3987 AccountPrivate *priv;
3988 GList *lot_list;
3989 GList *retval = nullptr;
3990
3991 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
3992
3993 priv = GET_PRIVATE(acc);
3994 for (lot_list = priv->lots; lot_list; lot_list = lot_list->next)
3995 {
3996 GNCLot *lot = static_cast<GNCLot*>(lot_list->data);
3997
3998 /* If this lot is closed, then ignore it */
3999 if (gnc_lot_is_closed (lot))
4000 continue;
4001
4002 if (match_func && !(match_func)(lot, user_data))
4003 continue;
4004
4005 /* Ok, this is a valid lot. Add it to our list of lots */
4006 retval = g_list_prepend (retval, lot);
4007 }
4008
4009 if (sort_func)
4010 retval = g_list_sort (retval, sort_func);
4011
4012 return retval;
4013}
4014
4015gpointer
4016xaccAccountForEachLot(const Account *acc,
4017 gpointer (*proc)(GNCLot *lot, void *data), void *data)
4018{
4019 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
4020 g_return_val_if_fail(proc, nullptr);
4021
4022 for (auto node = GET_PRIVATE(acc)->lots; node; node = node->next)
4023 if (auto result = proc(GNC_LOT(node->data), data))
4024 return result;
4025
4026 return nullptr;
4027}
4028
4029
4030/********************************************************************\
4031\********************************************************************/
4032
4033/* These functions use interchange gint64 and gboolean. Is that right? */
4034gboolean
4036{
4037 return get_kvp_boolean_path(acc, {"tax-related"});
4038}
4039
4040void
4041xaccAccountSetTaxRelated (Account *acc, gboolean tax_related)
4042{
4043 set_kvp_boolean_path(acc, {"tax-related"}, tax_related);
4044}
4045
4046const char *
4048{
4049 return get_kvp_string_path (acc, {"tax-US", "code"});
4050}
4051
4052void
4053xaccAccountSetTaxUSCode (Account *acc, const char *code)
4054{
4055 set_kvp_string_path (acc, {"tax-US", "code"}, code);
4056}
4057
4058const char *
4060{
4061 return get_kvp_string_path (acc, {"tax-US", "payer-name-source"});
4062}
4063
4064void
4066{
4067 set_kvp_string_path (acc, {"tax-US", "payer-name-source"}, source);
4068}
4069
4070gint64
4072{
4073 auto copy_number = get_kvp_int64_path (acc, {"tax-US", "copy-number"});
4074 return (copy_number && (*copy_number != 0)) ? *copy_number : 1;
4075}
4076
4077void
4078xaccAccountSetTaxUSCopyNumber (Account *acc, gint64 copy_number)
4079{
4080 if (copy_number != 0)
4081 set_kvp_int64_path (acc, {"tax-US", "copy-number"}, copy_number);
4082 else
4083 /* deletes KVP if it exists */
4084 set_kvp_int64_path (acc, {"tax-US", "copy-number"}, std::nullopt);
4085}
4086
4087/*********************************************************************\
4088\ ********************************************************************/
4089
4090
4092{
4093 if (gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_ACCOUNTING_LABELS))
4094 return _(dflt_acct_debit_str);
4095
4096 auto result = gnc_acct_debit_strs.find(acct_type);
4097 if (result != gnc_acct_debit_strs.end())
4098 return _(result->second);
4099 else
4100 return _(dflt_acct_debit_str);
4101}
4102
4104{
4105 if (gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_ACCOUNTING_LABELS))
4106 return _(dflt_acct_credit_str);
4107
4108 auto result = gnc_acct_credit_strs.find(acct_type);
4109 if (result != gnc_acct_credit_strs.end())
4110 return _(result->second);
4111 else
4112 return _(dflt_acct_credit_str);
4113}
4114
4115/********************************************************************\
4116\********************************************************************/
4117
4118gboolean
4120{
4121 return get_kvp_boolean_path(acc, {"placeholder"});
4122}
4123
4124void
4126{
4127 set_kvp_boolean_path(acc, {"placeholder"}, val);
4128}
4129
4130gboolean
4132{
4133 return get_kvp_boolean_path(acc, {"import-append-text"});
4134}
4135
4136void
4138{
4139 set_kvp_boolean_path(acc, {"import-append-text"}, val);
4140}
4141
4142gboolean
4144{
4145 g_return_val_if_fail (GNC_IS_ACCOUNT(acc), false);
4146 if (GET_PRIVATE(acc)->type != ACCT_TYPE_EQUITY)
4147 return false;
4148
4149 return !g_strcmp0 (get_kvp_string_path (acc, {"equity-type"}), "opening-balance");
4150}
4151
4152void
4154{
4155 g_return_if_fail (GNC_IS_ACCOUNT(acc));
4156 if (GET_PRIVATE(acc)->type != ACCT_TYPE_EQUITY)
4157 return;
4158 set_kvp_string_path(acc, {"equity-type"}, val ? "opening-balance" : nullptr);
4159}
4160
4163{
4164 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), PLACEHOLDER_NONE);
4165 if (xaccAccountGetPlaceholder(acc)) return PLACEHOLDER_THIS;
4166
4167 return gnc_account_foreach_descendant_until (acc, (AccountCb2)xaccAccountGetPlaceholder, nullptr)
4168 ? PLACEHOLDER_CHILD : PLACEHOLDER_NONE;
4169}
4170
4171/********************************************************************\
4172 \********************************************************************/
4173
4174gboolean
4176{
4177 return get_kvp_boolean_path (acc, {KEY_RECONCILE_INFO, "auto-interest-transfer"});
4178}
4179
4180void
4182{
4183 set_kvp_boolean_path (acc, {KEY_RECONCILE_INFO, "auto-interest-transfer"}, val);
4184}
4185
4186/********************************************************************\
4187\********************************************************************/
4188
4189gboolean
4191{
4192 return get_kvp_boolean_path (acc, {"hidden"});
4193}
4194
4195void
4196xaccAccountSetHidden (Account *acc, gboolean val)
4197{
4198 set_kvp_boolean_path (acc, {"hidden"}, val);
4199}
4200
4201gboolean
4203{
4204 AccountPrivate *priv;
4205
4206 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
4207
4208 if (xaccAccountGetHidden(acc))
4209 return TRUE;
4210 priv = GET_PRIVATE(acc);
4211 while ((acc = priv->parent) != nullptr)
4212 {
4213 priv = GET_PRIVATE(acc);
4214 if (xaccAccountGetHidden(acc))
4215 return TRUE;
4216 }
4217 return FALSE;
4218}
4219
4220/********************************************************************\
4221\********************************************************************/
4222
4223gboolean
4224xaccAccountHasAncestor (const Account *acc, const Account * ancestor)
4225{
4226 const Account *parent;
4227
4228 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
4229 g_return_val_if_fail(GNC_IS_ACCOUNT(ancestor), FALSE);
4230
4231 parent = acc;
4232 while (parent && parent != ancestor)
4233 parent = GET_PRIVATE(parent)->parent;
4234
4235 return (parent == ancestor);
4236}
4237
4238/********************************************************************\
4239\********************************************************************/
4240
4241/* You must edit the functions in this block in tandem. KEEP THEM IN
4242 SYNC! */
4243
4244#define GNC_RETURN_ENUM_AS_STRING(x) case (ACCT_TYPE_ ## x): return #x;
4245
4246const char *
4248{
4249 switch (type)
4250 {
4251 GNC_RETURN_ENUM_AS_STRING(NONE);
4252 GNC_RETURN_ENUM_AS_STRING(BANK);
4253 GNC_RETURN_ENUM_AS_STRING(CASH);
4254 GNC_RETURN_ENUM_AS_STRING(CREDIT);
4255 GNC_RETURN_ENUM_AS_STRING(ASSET);
4256 GNC_RETURN_ENUM_AS_STRING(LIABILITY);
4257 GNC_RETURN_ENUM_AS_STRING(STOCK);
4258 GNC_RETURN_ENUM_AS_STRING(MUTUAL);
4259 GNC_RETURN_ENUM_AS_STRING(CURRENCY);
4260 GNC_RETURN_ENUM_AS_STRING(INCOME);
4261 GNC_RETURN_ENUM_AS_STRING(EXPENSE);
4262 GNC_RETURN_ENUM_AS_STRING(EQUITY);
4263 GNC_RETURN_ENUM_AS_STRING(RECEIVABLE);
4264 GNC_RETURN_ENUM_AS_STRING(PAYABLE);
4265 GNC_RETURN_ENUM_AS_STRING(ROOT);
4266 GNC_RETURN_ENUM_AS_STRING(TRADING);
4267 GNC_RETURN_ENUM_AS_STRING(CHECKING);
4268 GNC_RETURN_ENUM_AS_STRING(SAVINGS);
4269 GNC_RETURN_ENUM_AS_STRING(MONEYMRKT);
4270 GNC_RETURN_ENUM_AS_STRING(CREDITLINE);
4271 default:
4272 PERR ("asked to translate unknown account type %d.\n", type);
4273 break;
4274 }
4275 return(nullptr);
4276}
4277
4278#undef GNC_RETURN_ENUM_AS_STRING
4279
4280#define GNC_RETURN_ON_MATCH(x) \
4281 if(g_strcmp0(#x, (str)) == 0) { *type = ACCT_TYPE_ ## x; return(TRUE); }
4282
4283gboolean
4285{
4286
4287 GNC_RETURN_ON_MATCH(NONE);
4288 GNC_RETURN_ON_MATCH(BANK);
4289 GNC_RETURN_ON_MATCH(CASH);
4290 GNC_RETURN_ON_MATCH(CREDIT);
4291 GNC_RETURN_ON_MATCH(ASSET);
4292 GNC_RETURN_ON_MATCH(LIABILITY);
4293 GNC_RETURN_ON_MATCH(STOCK);
4294 GNC_RETURN_ON_MATCH(MUTUAL);
4295 GNC_RETURN_ON_MATCH(CURRENCY);
4296 GNC_RETURN_ON_MATCH(INCOME);
4297 GNC_RETURN_ON_MATCH(EXPENSE);
4298 GNC_RETURN_ON_MATCH(EQUITY);
4299 GNC_RETURN_ON_MATCH(RECEIVABLE);
4300 GNC_RETURN_ON_MATCH(PAYABLE);
4301 GNC_RETURN_ON_MATCH(ROOT);
4302 GNC_RETURN_ON_MATCH(TRADING);
4303 GNC_RETURN_ON_MATCH(CHECKING);
4304 GNC_RETURN_ON_MATCH(SAVINGS);
4305 GNC_RETURN_ON_MATCH(MONEYMRKT);
4306 GNC_RETURN_ON_MATCH(CREDITLINE);
4307
4308 PERR("asked to translate unknown account type string %s.\n",
4309 str ? str : "(null)");
4310
4311 return(FALSE);
4312}
4313
4314#undef GNC_RETURN_ON_MATCH
4315
4316/* impedance mismatch is a source of loss */
4319{
4320 GNCAccountType type;
4321 gboolean rc;
4322 rc = xaccAccountStringToType(str, &type);
4323 if (FALSE == rc) return ACCT_TYPE_INVALID;
4324 return type;
4325}
4326
4327/********************************************************************\
4328\********************************************************************/
4329
4330static char const *
4331account_type_name[NUM_ACCOUNT_TYPES] =
4332{
4333 N_("Bank"),
4334 N_("Cash"),
4335 N_("Asset"),
4336 N_("Credit Card"),
4337 N_("Liability"),
4338 N_("Stock"),
4339 N_("Mutual Fund"),
4340 N_("Currency"),
4341 N_("Income"),
4342 N_("Expense"),
4343 N_("Equity"),
4344 N_("A/Receivable"),
4345 N_("A/Payable"),
4346 N_("Root"),
4347 N_("Trading")
4348 /*
4349 N_("Checking"),
4350 N_("Savings"),
4351 N_("Money Market"),
4352 N_("Credit Line")
4353 */
4354};
4355
4356const char *
4358{
4359 if (type < 0 || NUM_ACCOUNT_TYPES <= type ) return "";
4360 return _(account_type_name [type]);
4361}
4362
4363/********************************************************************\
4364\********************************************************************/
4365
4366guint32
4368{
4369 switch (type)
4370 {
4371 case ACCT_TYPE_BANK:
4372 case ACCT_TYPE_CASH:
4373 case ACCT_TYPE_ASSET:
4374 case ACCT_TYPE_CREDIT:
4376 case ACCT_TYPE_INCOME:
4377 case ACCT_TYPE_EXPENSE:
4378 case ACCT_TYPE_EQUITY:
4379 return
4380 (1 << ACCT_TYPE_BANK) |
4381 (1 << ACCT_TYPE_CASH) |
4382 (1 << ACCT_TYPE_ASSET) |
4383 (1 << ACCT_TYPE_CREDIT) |
4384 (1 << ACCT_TYPE_LIABILITY) |
4385 (1 << ACCT_TYPE_INCOME) |
4386 (1 << ACCT_TYPE_EXPENSE) |
4387 (1 << ACCT_TYPE_EQUITY);
4388 case ACCT_TYPE_STOCK:
4389 case ACCT_TYPE_MUTUAL:
4390 case ACCT_TYPE_CURRENCY:
4391 return
4392 (1 << ACCT_TYPE_STOCK) |
4393 (1 << ACCT_TYPE_MUTUAL) |
4394 (1 << ACCT_TYPE_CURRENCY);
4396 return (1 << ACCT_TYPE_RECEIVABLE);
4397 case ACCT_TYPE_PAYABLE:
4398 return (1 << ACCT_TYPE_PAYABLE);
4399 case ACCT_TYPE_TRADING:
4400 return (1 << ACCT_TYPE_TRADING);
4401 default:
4402 PERR("bad account type: %d", type);
4403 return 0;
4404 }
4405}
4406guint32
4408{
4409 switch (type)
4410 {
4411 case ACCT_TYPE_BANK:
4412 case ACCT_TYPE_CASH:
4413 case ACCT_TYPE_ASSET:
4414 case ACCT_TYPE_STOCK:
4415 case ACCT_TYPE_MUTUAL:
4416 case ACCT_TYPE_CURRENCY:
4417 case ACCT_TYPE_CREDIT:
4420 case ACCT_TYPE_PAYABLE:
4421 return
4422 (1 << ACCT_TYPE_BANK) |
4423 (1 << ACCT_TYPE_CASH) |
4424 (1 << ACCT_TYPE_ASSET) |
4425 (1 << ACCT_TYPE_STOCK) |
4426 (1 << ACCT_TYPE_MUTUAL) |
4427 (1 << ACCT_TYPE_CURRENCY) |
4428 (1 << ACCT_TYPE_CREDIT) |
4429 (1 << ACCT_TYPE_LIABILITY) |
4430 (1 << ACCT_TYPE_RECEIVABLE) |
4431 (1 << ACCT_TYPE_PAYABLE) |
4432 (1 << ACCT_TYPE_ROOT);
4433 case ACCT_TYPE_INCOME:
4434 case ACCT_TYPE_EXPENSE:
4435 return
4436 (1 << ACCT_TYPE_INCOME) |
4437 (1 << ACCT_TYPE_EXPENSE) |
4438 (1 << ACCT_TYPE_ROOT);
4439 case ACCT_TYPE_EQUITY:
4440 return
4441 (1 << ACCT_TYPE_EQUITY) |
4442 (1 << ACCT_TYPE_ROOT);
4443 case ACCT_TYPE_TRADING:
4444 return
4445 (1 << ACCT_TYPE_TRADING) |
4446 (1 << ACCT_TYPE_ROOT);
4447 default:
4448 PERR("bad account type: %d", type);
4449 return 0;
4450 }
4451}
4452
4453gboolean
4455 GNCAccountType child_type)
4456{
4457 /* ACCT_TYPE_NONE isn't compatible with anything, even ACCT_TYPE_NONE. */
4458 if (parent_type == ACCT_TYPE_NONE || child_type == ACCT_TYPE_NONE)
4459 return FALSE;
4460
4461 /* ACCT_TYPE_ROOT can't have a parent account, and asking will raise
4462 * an error. */
4463 if (child_type == ACCT_TYPE_ROOT)
4464 return FALSE;
4465
4466 return ((xaccParentAccountTypesCompatibleWith (child_type) &
4467 (1 << parent_type))
4468 != 0);
4469}
4470
4471guint32
4473{
4474 guint32 mask = (1 << NUM_ACCOUNT_TYPES) - 1;
4475 mask &= ~((1 << ACCT_TYPE_CURRENCY) | /* DEPRECATED */
4476 (1 << ACCT_TYPE_ROOT)); /* ROOT */
4477
4478 return mask;
4479}
4480
4482{
4483 switch (t)
4484 {
4486 case ACCT_TYPE_PAYABLE:
4487 return FALSE;
4488 default:
4491 }
4492}
4493
4496{
4497 switch (t)
4498 {
4499 case ACCT_TYPE_BANK:
4500 case ACCT_TYPE_STOCK:
4502 case ACCT_TYPE_CHECKING:
4503 case ACCT_TYPE_SAVINGS:
4504 case ACCT_TYPE_MUTUAL:
4505 case ACCT_TYPE_CURRENCY:
4506 case ACCT_TYPE_CASH:
4507 case ACCT_TYPE_ASSET:
4509 return ACCT_TYPE_ASSET;
4510 case ACCT_TYPE_CREDIT:
4512 case ACCT_TYPE_PAYABLE:
4514 return ACCT_TYPE_LIABILITY;
4515 case ACCT_TYPE_INCOME:
4516 return ACCT_TYPE_INCOME;
4517 case ACCT_TYPE_EXPENSE:
4518 return ACCT_TYPE_EXPENSE;
4519 case ACCT_TYPE_EQUITY:
4520 return ACCT_TYPE_EQUITY;
4521 case ACCT_TYPE_TRADING:
4522 default:
4523 return ACCT_TYPE_NONE;
4524 }
4525}
4526
4528{
4529 switch (t)
4530 {
4532 case ACCT_TYPE_PAYABLE:
4533 return TRUE;
4534 default:
4535 return FALSE;
4536 }
4537}
4538
4540{
4541 switch (t)
4542 {
4543 case ACCT_TYPE_EQUITY:
4544 return TRUE;
4545 default:
4546 return FALSE;
4547 }
4548}
4549
4550gboolean
4552{
4553 AccountPrivate *priv;
4554
4555 g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
4556
4557 priv = GET_PRIVATE(acc);
4558 return (priv->type == ACCT_TYPE_STOCK || priv->type == ACCT_TYPE_MUTUAL ||
4559 priv->type == ACCT_TYPE_CURRENCY);
4560}
4561
4562/********************************************************************\
4563\********************************************************************/
4564
4565gboolean
4567{
4568 gboolean retval = FALSE;
4569 auto date = get_kvp_int64_path (acc, {KEY_RECONCILE_INFO, "last-date"});
4570
4571 if (date)
4572 {
4573 if (last_date)
4574 *last_date = *date;
4575 retval = TRUE;
4576 }
4577 return retval;
4578}
4579
4580/********************************************************************\
4581\********************************************************************/
4582
4583void
4585{
4586 set_kvp_int64_path (acc, {KEY_RECONCILE_INFO, "last-date"}, last_date);
4587}
4588
4589/********************************************************************\
4590\********************************************************************/
4591
4592gboolean
4594 int *months, int *days)
4595{
4596 if (!acc) return FALSE;
4597 auto m{get_kvp_int64_path (acc, {KEY_RECONCILE_INFO, "last-interval", "months"})};
4598 auto d{get_kvp_int64_path (acc, {KEY_RECONCILE_INFO, "last-interval", "days"})};
4599 if (m && d)
4600 {
4601 if (months)
4602 *months = *m;
4603 if (days)
4604 *days = *d;
4605 return true;
4606 }
4607 return false;
4608}
4609
4610/********************************************************************\
4611\********************************************************************/
4612
4613void
4615{
4616 set_kvp_int64_path (acc, {KEY_RECONCILE_INFO, "last-interval", "months"}, months);
4617 set_kvp_int64_path (acc, {KEY_RECONCILE_INFO, "last-interval", "days"}, days);
4618}
4619
4620/********************************************************************\
4621\********************************************************************/
4622
4623gboolean
4625{
4626 if (auto date = get_kvp_int64_path (acc, {KEY_RECONCILE_INFO, KEY_POSTPONE, "date"}))
4627 {
4628 if (postpone_date)
4629 *postpone_date = *date;
4630 return true;
4631 }
4632 return false;
4633}
4634
4635/********************************************************************\
4636\********************************************************************/
4637
4638void
4640{
4641 set_kvp_int64_path (acc, {KEY_RECONCILE_INFO, KEY_POSTPONE, "date"}, postpone_date);
4642}
4643
4644/********************************************************************\
4645\********************************************************************/
4646
4647gboolean
4649 gnc_numeric *balance)
4650{
4651 if (auto bal = get_kvp_gnc_numeric_path (acc, {KEY_RECONCILE_INFO, KEY_POSTPONE, "balance"}))
4652 {
4653 if (balance)
4654 *balance = *bal;
4655 return true;
4656 }
4657 return false;
4658}
4659
4660/********************************************************************\
4661\********************************************************************/
4662
4663void
4665{
4666 set_kvp_gnc_numeric_path (acc, {KEY_RECONCILE_INFO, KEY_POSTPONE, "balance"}, balance);
4667}
4668
4669/********************************************************************\
4670
4671\********************************************************************/
4672
4673void
4675{
4676 set_kvp_gnc_numeric_path (acc, {KEY_RECONCILE_INFO, KEY_POSTPONE}, {});
4677}
4678
4679/********************************************************************\
4680\********************************************************************/
4681
4682const char *
4684{
4685 return get_kvp_string_path (acc, {"last-num"});
4686}
4687
4688/********************************************************************\
4689\********************************************************************/
4690
4691void
4692xaccAccountSetLastNum (Account *acc, const char *num)
4693{
4694 set_kvp_string_path (acc, {"last-num"}, num);
4695}
4696
4697
4698/********************************************************************\
4699\********************************************************************/
4700
4701static bool
4702get_balance_limit (const Account* acc, const std::string& key, gnc_numeric* balance)
4703{
4704 auto limit = get_kvp_gnc_numeric_path (acc, {KEY_BALANCE_LIMIT, key});
4705 if (limit)
4706 *balance = gnc_numeric_create (limit->num, limit->denom);
4707 return limit.has_value();
4708}
4709
4710static void
4711set_balance_limit (Account *acc, const std::string& key, std::optional<gnc_numeric> balance)
4712{
4713 if (balance && gnc_numeric_check (*balance))
4714 return;
4715 set_kvp_gnc_numeric_path (acc, {KEY_BALANCE_LIMIT, key}, balance);
4716}
4717
4718gboolean
4720 gnc_numeric *balance)
4721{
4722 return get_balance_limit (acc, KEY_BALANCE_HIGHER_LIMIT_VALUE, balance);
4723}
4724
4725gboolean
4727 gnc_numeric *balance)
4728{
4729 return get_balance_limit (acc, KEY_BALANCE_LOWER_LIMIT_VALUE, balance);
4730}
4731
4732void
4734{
4735 set_balance_limit (acc, KEY_BALANCE_HIGHER_LIMIT_VALUE, balance);
4736}
4737
4738void
4739xaccAccountSetLowerBalanceLimit (Account *acc, gnc_numeric balance)
4740{
4741 set_balance_limit (acc, KEY_BALANCE_LOWER_LIMIT_VALUE, balance);
4742}
4743
4744void
4746{
4747 set_balance_limit (acc, KEY_BALANCE_HIGHER_LIMIT_VALUE, {});
4748}
4749
4750void
4752{
4753 set_balance_limit (acc, KEY_BALANCE_LOWER_LIMIT_VALUE, {});
4754}
4755
4756gboolean
4758{
4759 return get_kvp_boolean_path (acc, {KEY_BALANCE_LIMIT, KEY_BALANCE_INCLUDE_SUB_ACCTS});
4760}
4761
4762void
4764{
4765 set_kvp_boolean_path (acc, {KEY_BALANCE_LIMIT, KEY_BALANCE_INCLUDE_SUB_ACCTS}, inc_sub);
4766}
4767
4768/********************************************************************\
4769\********************************************************************/
4770
4771static Account *
4772GetOrMakeOrphanAccount (Account *root, gnc_commodity * currency)
4773{
4774 char * accname;
4775 Account * acc;
4776
4777 g_return_val_if_fail (root, nullptr);
4778
4779 /* build the account name */
4780 if (!currency)
4781 {
4782 PERR ("No currency specified!");
4783 return nullptr;
4784 }
4785
4786 accname = g_strconcat (_("Orphaned Gains"), "-",
4787 gnc_commodity_get_mnemonic (currency), nullptr);
4788
4789 /* See if we've got one of these going already ... */
4790 acc = gnc_account_lookup_by_name(root, accname);
4791
4792 if (acc == nullptr)
4793 {
4794 /* Guess not. We'll have to build one. */
4795 acc = xaccMallocAccount (gnc_account_get_book(root));
4797 xaccAccountSetName (acc, accname);
4798 xaccAccountSetCommodity (acc, currency);
4799 xaccAccountSetType (acc, ACCT_TYPE_INCOME);
4800 xaccAccountSetDescription (acc, _("Realized Gain/Loss"));
4802 _("Realized Gains or Losses from "
4803 "Commodity or Trading Accounts "
4804 "that haven't been recorded elsewhere."));
4805
4806 /* Hang the account off the root. */
4807 gnc_account_append_child (root, acc);
4809 }
4810
4811 g_free (accname);
4812
4813 return acc;
4814}
4815
4816Account *
4817xaccAccountGainsAccount (Account *acc, gnc_commodity *curr)
4818{
4819 Path path {KEY_LOT_MGMT, "gains-acct", gnc_commodity_get_unique_name (curr)};
4820 auto gains_account = get_kvp_account_path (acc, path);
4821
4822 if (gains_account == nullptr) /* No gains account for this currency */
4823 {
4824 gains_account = GetOrMakeOrphanAccount (gnc_account_get_root (acc), curr);
4825 set_kvp_account_path (acc, path, gains_account);
4826 }
4827
4828 return gains_account;
4829}
4830
4831/********************************************************************\
4832\********************************************************************/
4833
4834void
4835dxaccAccountSetPriceSrc(Account *acc, const char *src)
4836{
4837 if (!acc) return;
4838
4839 if (xaccAccountIsPriced(acc))
4840 set_kvp_string_path (acc, {"old-price-source"}, src);
4841}
4842
4843/********************************************************************\
4844\********************************************************************/
4845
4846const char*
4848{
4849 if (!acc) return nullptr;
4850
4851 if (!xaccAccountIsPriced(acc)) return nullptr;
4852
4853 return get_kvp_string_path (acc, {"old-price-source"});
4854}
4855
4856/********************************************************************\
4857\********************************************************************/
4858
4859void
4860dxaccAccountSetQuoteTZ(Account *acc, const char *tz)
4861{
4862 if (!acc) return;
4863 if (!xaccAccountIsPriced(acc)) return;
4864 set_kvp_string_path (acc, {"old-quote-tz"}, tz);
4865}
4866
4867/********************************************************************\
4868\********************************************************************/
4869
4870const char*
4872{
4873 if (!acc) return nullptr;
4874 if (!xaccAccountIsPriced(acc)) return nullptr;
4875 return get_kvp_string_path (acc, {"old-quote-tz"});
4876}
4877
4878/********************************************************************\
4879\********************************************************************/
4880
4881void
4883{
4884 /* Would have been nice to use G_TYPE_BOOLEAN, but the other
4885 * boolean kvps save the value as "true" or "false" and that would
4886 * be file-incompatible with this.
4887 */
4888 set_kvp_int64_path (acc, {KEY_RECONCILE_INFO, KEY_INCLUDE_CHILDREN}, status);
4889}
4890
4891/********************************************************************\
4892\********************************************************************/
4893
4894gboolean
4896{
4897 /* access the account's kvp-data for status and return that, if no value
4898 * is found then we can assume not to include the children, that being
4899 * the default behaviour
4900 */
4901 return get_kvp_boolean_path (acc, {KEY_RECONCILE_INFO, KEY_INCLUDE_CHILDREN});
4902}
4903
4904/********************************************************************\
4905\********************************************************************/
4906
4907Split *
4908xaccAccountFindSplitByDesc(const Account *acc, const char *description)
4909{
4910 auto has_description = [description](const Split* s) -> bool
4911 { return !g_strcmp0 (description, xaccTransGetDescription (xaccSplitGetParent (s))); };
4912 return gnc_account_find_split (acc, has_description, true);
4913}
4914
4915/* This routine is for finding a matching transaction in an account by
4916 * matching on the description field. [CAS: The rest of this comment
4917 * seems to belong somewhere else.] This routine is used for
4918 * auto-filling in registers with a default leading account. The
4919 * dest_trans is a transaction used for currency checking. */
4920Transaction *
4921xaccAccountFindTransByDesc(const Account *acc, const char *description)
4922{
4923 auto split = xaccAccountFindSplitByDesc (acc, description);
4924 return split ? xaccSplitGetParent (split) : nullptr;
4925}
4926
4927/* ================================================================ */
4928/* Concatenation, Merging functions */
4929
4930void
4932{
4933
4934 /* errors */
4935 g_return_if_fail(GNC_IS_ACCOUNT(to_parent));
4936 g_return_if_fail(GNC_IS_ACCOUNT(from_parent));
4937
4938 /* optimizations */
4939 auto from_priv = GET_PRIVATE(from_parent);
4940 if (from_priv->children.empty())
4941 return;
4942
4943 ENTER (" ");
4944 auto children = from_priv->children;
4945 for (auto child : children)
4946 gnc_account_append_child(to_parent, child);
4947 LEAVE (" ");
4948}
4949/********************************************************************\
4950\********************************************************************/
4951
4952void
4954{
4955 g_return_if_fail(GNC_IS_ACCOUNT(parent));
4956
4957 auto ppriv = GET_PRIVATE(parent);
4958 for (auto it_a = ppriv->children.begin(); it_a != ppriv->children.end(); it_a++)
4959 {
4960 auto acc_a = *it_a;
4961 auto priv_a = GET_PRIVATE(acc_a);
4962 for (auto it_b = std::next(it_a); it_b != ppriv->children.end(); it_b++)
4963 {
4964 auto acc_b = *it_b;
4965 auto priv_b = GET_PRIVATE(acc_b);
4966 if (0 != null_strcmp(priv_a->accountName, priv_b->accountName))
4967 continue;
4968 if (0 != null_strcmp(priv_a->accountCode, priv_b->accountCode))
4969 continue;
4970 if (0 != null_strcmp(priv_a->description, priv_b->description))
4971 continue;
4972 if (0 != null_strcmp(xaccAccountGetColor(acc_a),
4973 xaccAccountGetColor(acc_b)))
4974 continue;
4975 if (!gnc_commodity_equiv(priv_a->commodity, priv_b->commodity))
4976 continue;
4977 if (0 != null_strcmp(xaccAccountGetNotes(acc_a),
4978 xaccAccountGetNotes(acc_b)))
4979 continue;
4980 if (priv_a->type != priv_b->type)
4981 continue;
4982
4983 /* consolidate children */
4984 if (!priv_b->children.empty())
4985 {
4986 auto work = priv_b->children;
4987 for (auto w : work)
4988 gnc_account_append_child (acc_a, w);
4989
4990 qof_event_gen (&acc_a->inst, QOF_EVENT_MODIFY, nullptr);
4991 qof_event_gen (&acc_b->inst, QOF_EVENT_MODIFY, nullptr);
4992 }
4993
4994 /* recurse to do the children's children */
4996
4997 /* consolidate transactions */
4998 while (!priv_b->splits.empty())
4999 xaccSplitSetAccount (priv_b->splits.front(), acc_a);
5000
5001 /* move back one before removal. next iteration around the loop
5002 * will get the node after node_b */
5003 it_b--;
5004
5005 /* The destroy function will remove from list -- node_a is ok,
5006 * it's before node_b */
5007 xaccAccountBeginEdit (acc_b);
5008 xaccAccountDestroy (acc_b);
5009 }
5010 }
5011}
5012
5013/* ================================================================ */
5014/* Transaction Traversal functions */
5015
5016
5017static void
5018xaccSplitsBeginStagedTransactionTraversals (SplitsVec& splits)
5019{
5020 for (auto s : splits)
5021 {
5022 Transaction *trans = s->parent;
5023
5024 if (trans)
5025 trans->marker = 0;
5026 }
5027}
5028
5029/* original function */
5030void
5032{
5033 if (!account)
5034 return;
5035 xaccSplitsBeginStagedTransactionTraversals(GET_PRIVATE (account)->splits);
5036}
5037
5038gboolean
5039xaccTransactionTraverse (Transaction *trans, int stage)
5040{
5041 if (trans == nullptr) return FALSE;
5042
5043 if (trans->marker < stage)
5044 {
5045 trans->marker = stage;
5046 return TRUE;
5047 }
5048
5049 return FALSE;
5050}
5051
5052/* Replacement for xaccGroupBeginStagedTransactionTraversals */
5053void
5055{
5056 auto do_one_account = [](auto acc)
5057 { gnc_account_foreach_split (acc, [](auto s){ s->parent->marker = 0; }); };
5058 gnc_account_foreach_descendant (account, do_one_account);
5059}
5060
5061int
5063 unsigned int stage,
5064 TransactionCallback thunk,
5065 void *cb_data)
5066{
5067 if (!acc) return 0;
5068
5069 // iterate on copy of splits. some callers modify the splitsvec.
5070 auto splits = GET_PRIVATE(acc)->splits;
5071 for (auto s : splits)
5072 {
5073 auto trans = s->parent;
5074 if (trans && (trans->marker < stage))
5075 {
5076 trans->marker = stage;
5077 if (thunk)
5078 {
5079 auto retval = thunk(trans, cb_data);
5080 if (retval) return retval;
5081 }
5082 }
5083 }
5084
5085 return 0;
5086}
5087
5088int
5090 unsigned int stage,
5091 TransactionCallback thunk,
5092 void *cb_data)
5093{
5094 const AccountPrivate *priv;
5095 Transaction *trans;
5096 int retval;
5097
5098 if (!acc) return 0;
5099
5100 /* depth first traversal */
5101 priv = GET_PRIVATE(acc);
5102 for (auto acc_p : priv->children)
5103 {
5104 retval = gnc_account_tree_staged_transaction_traversal(acc_p, stage, thunk, cb_data);
5105 if (retval) return retval;
5106 }
5107
5108 /* Now this account */
5109 for (auto s : priv->splits)
5110 {
5111 trans = s->parent;
5112 if (trans && (trans->marker < stage))
5113 {
5114 trans->marker = stage;
5115 if (thunk)
5116 {
5117 retval = thunk(trans, cb_data);
5118 if (retval) return retval;
5119 }
5120 }
5121 }
5122
5123 return 0;
5124}
5125
5126time64
5128{
5129 g_return_val_if_fail (GNC_IS_ACCOUNT(account), INT64_MAX);
5130 const auto& splits = xaccAccountGetSplits (account);
5131 return splits.empty() ? INT64_MAX : xaccTransGetDate (xaccSplitGetParent (splits.front()));
5132}
5133
5134/********************************************************************\
5135\********************************************************************/
5136
5137int
5138xaccAccountTreeForEachTransaction (Account *acc,
5139 int (*proc)(Transaction *t, void *data),
5140 void *data)
5141{
5142 if (!acc || !proc) return 0;
5143
5145 return gnc_account_tree_staged_transaction_traversal (acc, 42, proc, data);
5146}
5147
5148
5149gint
5150xaccAccountForEachTransaction(const Account *acc, TransactionCallback proc,
5151 void *data)
5152{
5153 if (!acc || !proc) return 0;
5155 return xaccAccountStagedTransactionTraversal(acc, 42, proc, data);
5156}
5157
5158/* ================================================================ */
5159/* The following functions are used by
5160 * src/import-export/import-backend.c to manipulate the contra-account
5161 * matching data. See src/import-export/import-backend.c for explanations.
5162 */
5163
5164#define IMAP_FRAME "import-map"
5165#define IMAP_FRAME_BAYES "import-map-bayes"
5166
5167/* Look up an Account in the map */
5168Account*
5169gnc_account_imap_find_account (Account *acc,
5170 const char *category,
5171 const char *key)
5172{
5173 if (!acc || !key) return nullptr;
5174 std::vector<std::string> path {IMAP_FRAME};
5175 if (category)
5176 path.push_back (category);
5177 path.push_back (key);
5178 return get_kvp_account_path (acc, path);
5179}
5180
5181Account*
5182gnc_account_imap_find_any (QofBook *book, const char* category, const char *key)
5183{
5184 Account *account = nullptr;
5185
5186 /* Get list of Accounts */
5187 auto root = gnc_book_get_root_account (book);
5188 auto accts = gnc_account_get_descendants_sorted (root);
5189
5190 /* Go through list of accounts */
5191 for (auto ptr = accts; ptr; ptr = g_list_next (ptr))
5192 {
5193 auto tmp_acc = static_cast<Account*> (ptr->data);
5194
5195 if (gnc_account_imap_find_account (tmp_acc, category, key))
5196 {
5197 account = tmp_acc;
5198 break;
5199 }
5200 }
5201 g_list_free (accts);
5202
5203return account;
5204}
5205
5206/* Store an Account in the map */
5207void
5208gnc_account_imap_add_account (Account *acc,
5209 const char *category,
5210 const char *key,
5211 Account *added_acc)
5212{
5213 if (!acc || !key || !added_acc || !*key) return;
5214
5215 auto path = category ? Path{IMAP_FRAME, category, key} : Path{IMAP_FRAME, key};
5216
5217 set_kvp_account_path (acc, path, added_acc);
5218}
5219
5220/* Remove a reference to an Account in the map */
5221void
5222gnc_account_imap_delete_account (Account *acc,
5223 const char *category,
5224 const char *key)
5225{
5226 if (!acc || !key) return;
5227
5228 auto path = category ? Path{IMAP_FRAME, category, key} : Path{IMAP_FRAME, key};
5229 if (qof_instance_has_path_slot (QOF_INSTANCE (acc), path))
5230 {
5231 qof_instance_slot_path_delete (QOF_INSTANCE (acc), path);
5232 if (category)
5233 qof_instance_slot_path_delete_if_empty (QOF_INSTANCE (acc), {IMAP_FRAME, category});
5234 qof_instance_slot_path_delete_if_empty (QOF_INSTANCE (acc), {IMAP_FRAME});
5235 }
5236 qof_instance_set_dirty (QOF_INSTANCE (acc));
5237}
5238
5239/*--------------------------------------------------------------------------
5240 Below here is the bayes transaction to account matching system
5241--------------------------------------------------------------------------*/
5242
5243
5249{
5250 double product; /* product of probabilities */
5251 double product_difference; /* product of (1-probabilities) */
5252};
5253
5255{
5256 std::string account_guid;
5257 int64_t token_count;
5258};
5259
5264{
5265 std::vector<AccountTokenCount> accounts;
5266 int64_t total_count;
5267};
5268
5273{
5274 std::string account_guid;
5275 int32_t probability;
5276};
5277
5278static void
5279build_token_info(char const * suffix, KvpValue * value, TokenAccountsInfo & tokenInfo)
5280{
5281 if (strlen(suffix) == GUID_ENCODING_LENGTH)
5282 {
5283 tokenInfo.total_count += value->get<int64_t>();
5284 /*By convention, the key ends with the account GUID.*/
5285 tokenInfo.accounts.emplace_back(AccountTokenCount{std::string{suffix}, value->get<int64_t>()});
5286 }
5287}
5288
5292static constexpr int probability_factor = 100000;
5293
5294static FinalProbabilityVec
5295build_probabilities(ProbabilityVec const & first_pass)
5296{
5297 FinalProbabilityVec ret;
5298 for (auto const & first_pass_prob : first_pass)
5299 {
5300 auto const & account_probability = first_pass_prob.second;
5301 /* P(AB) = A*B / [A*B + (1-A)*(1-B)]
5302 * NOTE: so we only keep track of a running product(A*B*C...)
5303 * and product difference ((1-A)(1-B)...)
5304 */
5305 int32_t probability = (account_probability.product /
5306 (account_probability.product + account_probability.product_difference)) * probability_factor;
5307 ret.push_back({first_pass_prob.first, probability});
5308 }
5309 return ret;
5310}
5311
5312static AccountInfo
5313highest_probability(FinalProbabilityVec const & probabilities)
5314{
5315 AccountInfo ret {"", std::numeric_limits<int32_t>::min()};
5316 for (auto const & prob : probabilities)
5317 if (prob.second > ret.probability)
5318 ret = AccountInfo {prob.first, prob.second};
5319 return ret;
5320}
5321
5322static ProbabilityVec
5323get_first_pass_probabilities(Account* acc, GList * tokens)
5324{
5325 ProbabilityVec ret;
5326 /* find the probability for each account that contains any of the tokens
5327 * in the input tokens list. */
5328 for (auto current_token = tokens; current_token; current_token = current_token->next)
5329 {
5330 TokenAccountsInfo tokenInfo{};
5331 auto path = std::string{IMAP_FRAME_BAYES "/"} + static_cast <char const *> (current_token->data) + "/";
5332 qof_instance_foreach_slot_prefix (QOF_INSTANCE (acc), path, &build_token_info, tokenInfo);
5333 for (auto const & current_account_token : tokenInfo.accounts)
5334 {
5335 auto item = std::find_if(ret.begin(), ret.end(), [&current_account_token]
5336 (std::pair<std::string, AccountProbability> const & a) {
5337 return current_account_token.account_guid == a.first;
5338 });
5339 if (item != ret.end())
5340 {/* This account is already in the map */
5341 item->second.product = ((double)current_account_token.token_count /
5342 (double)tokenInfo.total_count) * item->second.product;
5343 item->second.product_difference = ((double)1 - ((double)current_account_token.token_count /
5344 (double)tokenInfo.total_count)) * item->second.product_difference;
5345 }
5346 else
5347 {
5348 /* add a new entry */
5349 AccountProbability new_probability;
5350 new_probability.product = ((double)current_account_token.token_count /
5351 (double)tokenInfo.total_count);
5352 new_probability.product_difference = 1 - (new_probability.product);
5353 ret.push_back({current_account_token.account_guid, std::move(new_probability)});
5354 }
5355 } /* for all accounts in tokenInfo */
5356 }
5357 return ret;
5358}
5359
5360static std::string
5361look_for_old_separator_descendants (Account *root, std::string const & full_name, const gchar *separator)
5362{
5363 GList *top_accounts, *ptr;
5364 gint found_len = 0;
5365 gchar found_sep;
5366 top_accounts = gnc_account_get_descendants (root);
5367 PINFO("Incoming full_name is '%s', current separator is '%s'", full_name.c_str (), separator);
5368 /* Go through list of top level accounts */
5369 for (ptr = top_accounts; ptr; ptr = g_list_next (ptr))
5370 {
5371 const gchar *name = xaccAccountGetName (static_cast <Account const *> (ptr->data));
5372 // we are looking for the longest top level account that matches
5373 if (g_str_has_prefix (full_name.c_str (), name))
5374 {
5375 gint name_len = strlen (name);
5376 const gchar old_sep = full_name[name_len];
5377 if (!g_ascii_isalnum (old_sep)) // test for non alpha numeric
5378 {
5379 if (name_len > found_len)
5380 {
5381 found_sep = full_name[name_len];
5382 found_len = name_len;
5383 }
5384 }
5385 }
5386 }
5387 g_list_free (top_accounts); // Free the List
5388 std::string new_name {full_name};
5389 if (found_len > 1)
5390 std::replace (new_name.begin (), new_name.end (), found_sep, *separator);
5391 PINFO ("Return full_name is '%s'", new_name.c_str ());
5392 return new_name;
5393}
5394
5395static std::string
5396get_guid_from_account_name (Account * root, std::string const & name)
5397{
5398 auto map_account = gnc_account_lookup_by_full_name (root, name.c_str ());
5399 if (!map_account)
5400 {
5401 auto temp_account_name = look_for_old_separator_descendants (root, name,
5403 map_account = gnc_account_lookup_by_full_name (root, temp_account_name.c_str ());
5404 }
5405 auto temp_guid = gnc::GUID {*xaccAccountGetGUID (map_account)};
5406 return temp_guid.to_string ();
5407}
5408
5409static FlatKvpEntry
5410convert_entry (KvpEntry entry, Account* root)
5411{
5412 /*We need to make a copy here.*/
5413 auto account_name = entry.first.back();
5414 if (!gnc::GUID::is_valid_guid (account_name))
5415 {
5416 /* Earlier version stored the account name in the import map, and
5417 * there were early beta versions of 2.7 that stored a GUID.
5418 * If there is no GUID, we assume it's an account name. */
5419 /* Take off the account name and replace it with the GUID */
5420 entry.first.pop_back();
5421 auto guid_str = get_guid_from_account_name (root, account_name);
5422 entry.first.emplace_back (guid_str);
5423 }
5424 std::string new_key {std::accumulate (entry.first.begin(), entry.first.end(), std::string {})};
5425 new_key = IMAP_FRAME_BAYES + new_key;
5426 return {new_key, entry.second};
5427}
5428
5429static std::vector<FlatKvpEntry>
5430get_flat_imap (Account * acc)
5431{
5432 auto frame = qof_instance_get_slots (QOF_INSTANCE (acc));
5433 auto slot = frame->get_slot ({IMAP_FRAME_BAYES});
5434 if (!slot)
5435 return {};
5436 auto imap_frame = slot->get<KvpFrame*> ();
5437 auto flat_kvp = imap_frame->flatten_kvp ();
5438 auto root = gnc_account_get_root (acc);
5439 std::vector <FlatKvpEntry> ret;
5440 for (auto const & flat_entry : flat_kvp)
5441 {
5442 auto converted_entry = convert_entry (flat_entry, root);
5443 /*If the entry was invalid, we don't perpetuate it.*/
5444 if (converted_entry.first.size())
5445 ret.emplace_back (converted_entry);
5446 }
5447 return ret;
5448}
5449
5450static bool
5451convert_imap_account_bayes_to_flat (Account *acc)
5452{
5453 auto frame = qof_instance_get_slots (QOF_INSTANCE (acc));
5454 if (!frame->get_keys().size())
5455 return false;
5456 auto flat_imap = get_flat_imap(acc);
5457 if (!flat_imap.size ())
5458 return false;
5460 frame->set({IMAP_FRAME_BAYES}, nullptr);
5461 std::for_each(flat_imap.begin(), flat_imap.end(),
5462 [&frame] (FlatKvpEntry const & entry) {
5463 frame->set({entry.first.c_str()}, entry.second);
5464 });
5465 qof_instance_set_dirty (QOF_INSTANCE (acc));
5467 return true;
5468}
5469
5470/*
5471 * Checks for import map data and converts them when found.
5472 */
5473static bool
5474imap_convert_bayes_to_flat (QofBook * book)
5475{
5476 auto root = gnc_book_get_root_account (book);
5477 auto accts = gnc_account_get_descendants_sorted (root);
5478 bool ret = false;
5479 for (auto ptr = accts; ptr; ptr = g_list_next (ptr))
5480 {
5481 Account *acc = static_cast <Account*> (ptr->data);
5482 if (convert_imap_account_bayes_to_flat (acc))
5483 {
5484 ret = true;
5485 gnc_features_set_used (book, GNC_FEATURE_GUID_FLAT_BAYESIAN);
5486 }
5487 }
5488 g_list_free (accts);
5489 return ret;
5490}
5491
5492void
5494{
5495 imap_convert_bayes_to_flat_run = false;
5496}
5497
5498/*
5499 * Here we check to see the state of import map data.
5500 *
5501 * If the GUID_FLAT_BAYESIAN feature flag is set, everything
5502 * should be fine.
5503 *
5504 * If it is not set, there are two possibilities: import data
5505 * are present from a previous version or not. If they are,
5506 * they are converted, and the feature flag set. If there are
5507 * no previous data, nothing is done.
5508 */
5509static void
5510check_import_map_data (QofBook *book)
5511{
5512 if (gnc_features_check_used (book, GNC_FEATURE_GUID_FLAT_BAYESIAN) ||
5513 imap_convert_bayes_to_flat_run)
5514 return;
5515
5516 /* This function will set GNC_FEATURE_GUID_FLAT_BAYESIAN if necessary.*/
5517 imap_convert_bayes_to_flat (book);
5518 imap_convert_bayes_to_flat_run = true;
5519}
5520
5521static constexpr double threshold = .90 * probability_factor; /* 90% */
5522
5524Account*
5526{
5527 if (!acc)
5528 return nullptr;
5529 auto book = gnc_account_get_book(acc);
5530 check_import_map_data (book);
5531 auto first_pass = get_first_pass_probabilities(acc, tokens);
5532 if (!first_pass.size())
5533 return nullptr;
5534 auto final_probabilities = build_probabilities(first_pass);
5535 if (!final_probabilities.size())
5536 return nullptr;
5537 auto best = highest_probability(final_probabilities);
5538 if (best.account_guid == "")
5539 return nullptr;
5540 if (best.probability < threshold)
5541 return nullptr;
5542 gnc::GUID guid;
5543 try {
5544 guid = gnc::GUID::from_string(best.account_guid);
5545 } catch (gnc::guid_syntax_exception&) {
5546 return nullptr;
5547 }
5548 auto account = xaccAccountLookup (reinterpret_cast<GncGUID*>(&guid), book);
5549 return account;
5550}
5551
5552static void
5553change_imap_entry (Account *acc, std::string const & path, int64_t token_count)
5554{
5555 PINFO("Source Account is '%s', Count is '%" G_GINT64_FORMAT "'",
5556 xaccAccountGetName (acc), token_count);
5557
5558 // check for existing guid entry
5559 if (auto existing_token_count = get_kvp_int64_path (acc, {path}))
5560 {
5561 PINFO("found existing value of '%" G_GINT64_FORMAT "'", *existing_token_count);
5562 token_count += *existing_token_count;
5563 }
5564
5565 // Add or Update the entry based on guid
5566 set_kvp_int64_path (acc, {path}, token_count);
5567}
5568
5570void
5572 GList *tokens,
5573 Account *added_acc)
5574{
5575 GList *current_token;
5576 gint64 token_count;
5577 char *account_fullname;
5578 char *guid_string;
5579
5580 ENTER(" ");
5581 if (!acc)
5582 {
5583 LEAVE(" ");
5584 return;
5585 }
5586 check_import_map_data (gnc_account_get_book(acc));
5587
5588 g_return_if_fail (added_acc != nullptr);
5589 account_fullname = gnc_account_get_full_name(added_acc);
5591
5592 PINFO("account name: '%s'", account_fullname);
5593
5594 guid_string = guid_to_string (xaccAccountGetGUID (added_acc));
5595
5596 /* process each token in the list */
5597 for (current_token = g_list_first(tokens); current_token;
5598 current_token = current_token->next)
5599 {
5600 char* token = static_cast<char*>(current_token->data);
5601 /* Jump to next iteration if the pointer is not valid or if the
5602 string is empty. In HBCI import we almost always get an empty
5603 string, which doesn't work in the kvp loopkup later. So we
5604 skip this case here. */
5605 if (!token || !token[0])
5606 continue;
5607 /* start off with one token for this account */
5608 token_count = 1;
5609 PINFO("adding token '%s'", token);
5610 auto path = std::string {IMAP_FRAME_BAYES} + '/' + token + '/' + guid_string;
5611 /* change the imap entry for the account */
5612 change_imap_entry (acc, path, token_count);
5613 }
5614 /* free up the account fullname and guid string */
5616 gnc_features_set_used (gnc_account_get_book(acc), GNC_FEATURE_GUID_FLAT_BAYESIAN);
5617 g_free (account_fullname);
5618 g_free (guid_string);
5619 LEAVE(" ");
5620}
5621
5622/*******************************************************************************/
5623
5624static void
5625build_non_bayes (const char *key, const GValue *value, gpointer user_data)
5626{
5627 if (!G_VALUE_HOLDS_BOXED (value))
5628 return;
5629 QofBook *book;
5630 GncGUID *guid = nullptr;
5631 gchar *guid_string = nullptr;
5632 auto imapInfo = (GncImapInfo*)user_data;
5633 // Get the book
5634 book = qof_instance_get_book (imapInfo->source_account);
5635
5636 guid = (GncGUID*)g_value_get_boxed (value);
5637 guid_string = guid_to_string (guid);
5638
5639 PINFO("build_non_bayes: match string '%s', match account guid: '%s'",
5640 (char*)key, guid_string);
5641
5642 auto imapInfo_node = static_cast <GncImapInfo*> (g_malloc(sizeof(GncImapInfo)));
5643
5644 imapInfo_node->source_account = imapInfo->source_account;
5645 imapInfo_node->map_account = xaccAccountLookup (guid, book);
5646 imapInfo_node->head = g_strdup (imapInfo->head);
5647 imapInfo_node->match_string = g_strdup (key);
5648 imapInfo_node->category = g_strdup (imapInfo->category);
5649 imapInfo_node->count = g_strdup (" ");
5650
5651 imapInfo->list = g_list_prepend (imapInfo->list, imapInfo_node);
5652
5653 g_free (guid_string);
5654}
5655
5656static void
5657build_bayes (const char *suffix, KvpValue * value, GncImapInfo & imapInfo)
5658{
5659 size_t guid_start = strlen(suffix) - GUID_ENCODING_LENGTH;
5660 std::string account_guid {&suffix[guid_start]};
5661 GncGUID guid;
5662 try
5663 {
5664 guid = gnc::GUID::from_string (account_guid);
5665 }
5666 catch (const gnc::guid_syntax_exception& err)
5667 {
5668 PWARN("Invalid GUID string from %s%s", IMAP_FRAME_BAYES, suffix);
5669 }
5670 auto map_account = xaccAccountLookup (&guid, gnc_account_get_book (imapInfo.source_account));
5671 auto imap_node = static_cast <GncImapInfo*> (g_malloc (sizeof (GncImapInfo)));
5672 auto count = value->get <int64_t> ();
5673 imap_node->source_account = imapInfo.source_account;
5674 imap_node->map_account = map_account;
5675 imap_node->head = g_strdup_printf ("%s%s", IMAP_FRAME_BAYES, suffix);
5676 imap_node->match_string = g_strndup (&suffix[1], guid_start - 2);
5677 imap_node->category = g_strdup(" ");
5678 imap_node->count = g_strdup_printf ("%" G_GINT64_FORMAT, count);
5679 imapInfo.list = g_list_prepend (imapInfo.list, imap_node);
5680}
5681
5683{
5684 g_free (imapInfo->head);
5685 g_free (imapInfo->category);
5686 g_free (imapInfo->match_string);
5687 g_free (imapInfo->count);
5688 g_free (imapInfo);
5689}
5690
5691GList *
5693{
5694 check_import_map_data (gnc_account_get_book (acc));
5695 /* A dummy object which is used to hold the specified account, and the list
5696 * of data about which we care. */
5697 GncImapInfo imapInfo {acc, nullptr};
5698 qof_instance_foreach_slot_prefix (QOF_INSTANCE (acc), IMAP_FRAME_BAYES, &build_bayes, imapInfo);
5699 return g_list_reverse(imapInfo.list);
5700}
5701
5702GList *
5703gnc_account_imap_get_info (Account *acc, const char *category)
5704{
5705 GList *list = nullptr;
5706
5707 GncImapInfo imapInfo;
5708
5709 std::vector<std::string> path {IMAP_FRAME};
5710 if (category)
5711 path.emplace_back (category);
5712
5713 imapInfo.source_account = acc;
5714 imapInfo.list = list;
5715
5716 imapInfo.head = g_strdup (IMAP_FRAME);
5717 imapInfo.category = g_strdup (category);
5718
5719 if (qof_instance_has_path_slot (QOF_INSTANCE (acc), path))
5720 {
5721 qof_instance_foreach_slot (QOF_INSTANCE(acc), IMAP_FRAME, category,
5722 build_non_bayes, &imapInfo);
5723 }
5724 g_free (imapInfo.head);
5725 g_free (imapInfo.category);
5726 return g_list_reverse(imapInfo.list);
5727}
5728
5729/*******************************************************************************/
5730
5731gchar *
5732gnc_account_get_map_entry (Account *acc, const char *head, const char *category)
5733{
5734 return g_strdup (category ?
5735 get_kvp_string_path (acc, {head, category}) :
5736 get_kvp_string_path (acc, {head}));
5737}
5738
5739GncGUID *
5740gnc_account_get_map_guid_entry (Account *acc, const char *head, const char *category)
5741{
5742 return category ?
5743 get_kvp_guid_path (acc, {head, category}) :
5744 get_kvp_guid_path (acc, {head});
5745}
5746
5747void
5748gnc_account_delete_map_entry (Account *acc, char *head, char *category,
5749 char *match_string, gboolean empty)
5750{
5751 if (acc != nullptr)
5752 {
5753 std::vector<std::string> path {head};
5754 if (category)
5755 path.emplace_back (category);
5756 if (match_string)
5757 path.emplace_back (match_string);
5758
5759 if (qof_instance_has_path_slot (QOF_INSTANCE (acc), path))
5760 {
5762 if (empty)
5763 qof_instance_slot_path_delete_if_empty (QOF_INSTANCE(acc), path);
5764 else
5765 qof_instance_slot_path_delete (QOF_INSTANCE(acc), path);
5766 PINFO("Account is '%s', head is '%s', category is '%s', match_string is'%s'",
5767 xaccAccountGetName (acc), head, category, match_string);
5768 qof_instance_set_dirty (QOF_INSTANCE(acc));
5770 }
5771 }
5772}
5773
5774void
5776{
5777 if (acc != nullptr)
5778 {
5779 auto slots = qof_instance_get_slots_prefix (QOF_INSTANCE (acc), IMAP_FRAME_BAYES);
5780 if (!slots.size()) return;
5782 for (auto const & entry : slots)
5783 {
5784 qof_instance_slot_path_delete (QOF_INSTANCE (acc), {entry.first});
5785 }
5786 qof_instance_set_dirty (QOF_INSTANCE(acc));
5788 }
5789}
5790
5791/* ================================================================ */
5792/* QofObject function implementation and registration */
5793
5794static void
5795destroy_all_child_accounts (Account *acc, gpointer data)
5796{
5798 xaccAccountDestroy (acc);
5799}
5800
5801static void
5802gnc_account_book_end(QofBook* book)
5803{
5804 Account *root_account = gnc_book_get_root_account (book);
5805 GList *accounts;
5806
5807 if (!root_account)
5808 return;
5809
5810 accounts = gnc_account_get_descendants (root_account);
5811
5812 if (accounts)
5813 {
5814 accounts = g_list_reverse (accounts);
5815 g_list_foreach (accounts, (GFunc)destroy_all_child_accounts, nullptr);
5816 g_list_free (accounts);
5817 }
5818 xaccAccountBeginEdit (root_account);
5819 xaccAccountDestroy (root_account);
5820}
5821
5822#ifdef _MSC_VER
5823/* MSVC compiler doesn't have C99 "designated initializers"
5824 * so we wrap them in a macro that is empty on MSVC. */
5825# define DI(x) /* */
5826#else
5827# define DI(x) x
5828#endif
5829static QofObject account_object_def =
5830{
5831 DI(.interface_version = ) QOF_OBJECT_VERSION,
5832 DI(.e_type = ) GNC_ID_ACCOUNT,
5833 DI(.type_label = ) "Account",
5834 DI(.create = ) (void*(*)(QofBook*)) xaccMallocAccount,
5835 DI(.book_begin = ) nullptr,
5836 DI(.book_end = ) gnc_account_book_end,
5837 DI(.is_dirty = ) qof_collection_is_dirty,
5838 DI(.mark_clean = ) qof_collection_mark_clean,
5839 DI(.foreach = ) qof_collection_foreach,
5840 DI(.printable = ) (const char * (*)(gpointer)) xaccAccountGetName,
5841 DI(.version_cmp = ) (int (*)(gpointer, gpointer)) qof_instance_version_cmp,
5842};
5843
5844gboolean xaccAccountRegister (void)
5845{
5846 static QofParam params[] =
5847 {
5848 {
5849 ACCOUNT_NAME_, QOF_TYPE_STRING,
5852 },
5853 {
5854 ACCOUNT_CODE_, QOF_TYPE_STRING,
5857 },
5858 {
5859 ACCOUNT_DESCRIPTION_, QOF_TYPE_STRING,
5862 },
5863 {
5864 ACCOUNT_COLOR_, QOF_TYPE_STRING,
5867 },
5868 {
5869 ACCOUNT_FILTER_, QOF_TYPE_STRING,
5872 },
5873 {
5874 ACCOUNT_SORT_ORDER_, QOF_TYPE_STRING,
5877 },
5878 {
5879 ACCOUNT_SORT_REVERSED_, QOF_TYPE_BOOLEAN,
5882 },
5883 {
5884 ACCOUNT_NOTES_, QOF_TYPE_STRING,
5887 },
5888 {
5889 ACCOUNT_PRESENT_, QOF_TYPE_NUMERIC,
5890 (QofAccessFunc) xaccAccountGetPresentBalance, nullptr
5891 },
5892 {
5893 ACCOUNT_BALANCE_, QOF_TYPE_NUMERIC,
5895 },
5896 {
5897 ACCOUNT_CLEARED_, QOF_TYPE_NUMERIC,
5899 },
5900 {
5901 ACCOUNT_RECONCILED_, QOF_TYPE_NUMERIC,
5903 },
5904 {
5905 ACCOUNT_TYPE_, QOF_TYPE_STRING,
5906 (QofAccessFunc) qofAccountGetTypeString,
5907 (QofSetterFunc) qofAccountSetType
5908 },
5909 {
5910 ACCOUNT_FUTURE_MINIMUM_, QOF_TYPE_NUMERIC,
5911 (QofAccessFunc) xaccAccountGetProjectedMinimumBalance, nullptr
5912 },
5913 {
5914 ACCOUNT_TAX_RELATED, QOF_TYPE_BOOLEAN,
5917 },
5918 {
5919 ACCOUNT_OPENING_BALANCE_, QOF_TYPE_BOOLEAN,
5922 },
5923 {
5924 ACCOUNT_SCU, QOF_TYPE_INT32,
5927 },
5928 {
5929 ACCOUNT_NSCU, QOF_TYPE_BOOLEAN,
5932 },
5933 {
5934 ACCOUNT_PARENT, GNC_ID_ACCOUNT,
5936 (QofSetterFunc) qofAccountSetParent
5937 },
5938 {
5939 QOF_PARAM_BOOK, QOF_ID_BOOK,
5941 },
5942 {
5943 QOF_PARAM_GUID, QOF_TYPE_GUID,
5945 },
5946 { nullptr },
5947 };
5948
5949 qof_class_register (GNC_ID_ACCOUNT, (QofSortFunc) qof_xaccAccountOrder, params);
5950
5951 return qof_object_register (&account_object_def);
5952}
5953
5954/* ======================= UNIT TESTING ACCESS =======================
5955 * The following functions are for unit testing use only.
5956 */
5957static AccountPrivate*
5958utest_account_get_private (Account *acc)
5959{
5960 return GET_PRIVATE (acc);
5961}
5962
5964_utest_account_fill_functions(void)
5965{
5967
5968 func->get_private = utest_account_get_private;
5969 func->coll_get_root_account = gnc_coll_get_root_account;
5970 func->xaccFreeAccountChildren = xaccFreeAccountChildren;
5971 func->xaccFreeAccount = xaccFreeAccount;
5972 func->qofAccountSetParent = qofAccountSetParent;
5973 func->gnc_account_lookup_by_full_name_helper =
5974 gnc_account_lookup_by_full_name_helper;
5975
5976 return func;
5977}
5978/* ======================= 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:3289
void xaccAccountSetOnlineID(Account *acc, const char *id)
Set the account's online_id, the identifier (e.g.
Definition Account.cpp:2661
void xaccAccountRecomputeBalance(Account *acc)
The following recompute the partial balances (stored with the transaction) and the total balance,...
Definition Account.cpp:2275
const char * xaccAccountGetDescription(const Account *acc)
Get the account's description.
Definition Account.cpp:3343
void xaccAccountSetColor(Account *acc, const char *str)
Set the account's Color.
Definition Account.cpp:2613
gint gnc_account_get_current_depth(const Account *account)
Return the number of levels of this account below the root account.
Definition Account.cpp:3010
const char * xaccAccountGetColor(const Account *acc)
Get the account's color.
Definition Account.cpp:3350
gboolean xaccAccountHasAncestor(const Account *acc, const Account *ancestor)
Returns true if the account is 'ancestor' or has 'ancestor' as an ancestor.
Definition Account.cpp:4224
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:1516
void gnc_account_set_policy(Account *acc, GNCPolicy *policy)
Set the account's lot order policy.
Definition Account.cpp:2108
gboolean gnc_account_insert_split(Account *acc, Split *s)
Insert the given split from an account.
Definition Account.cpp:1943
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:3545
gboolean gnc_account_get_defer_bal_computation(Account *acc)
Get the account's flag for deferred balance computation.
Definition Account.cpp:1924
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:2942
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:3052
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:2983
void xaccAccountDestroyAllTransactions(Account *acc)
Destroy all of the transactions that parent splits in an account.
Definition Account.cpp:1600
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:1475
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:3305
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:3415
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:2836
GNCAccountType xaccAccountGetType(const Account *acc)
Returns the account's account type.
Definition Account.cpp:3267
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:2993
int xaccAccountGetCommoditySCUi(const Account *acc)
Return the 'internal' SCU setting.
Definition Account.cpp:2738
int xaccAccountGetCommoditySCU(const Account *acc)
Return the SCU for the account.
Definition Account.cpp:2745
void xaccAccountSetName(Account *acc, const char *str)
Set the account's name.
Definition Account.cpp:2458
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:3044
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:4931
void xaccAccountSetCommoditySCU(Account *acc, int scu)
Set the SCU for the account.
Definition Account.cpp:2722
void xaccAccountSetFilter(Account *acc, const char *str)
Set the account's Filter.
Definition Account.cpp:2619
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:4835
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:2953
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:4817
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:3533
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:1898
void xaccAccountSetNonStdSCU(Account *acc, gboolean flag)
Set the flag indicating that this account uses a non-standard SCU.
Definition Account.cpp:2758
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:3851
GList * gnc_account_get_children(const Account *account)
This routine returns a GList of all children accounts of the specified account.
Definition Account.cpp:2960
const char * xaccAccountGetSortOrder(const Account *acc)
Get the account's Sort Order.
Definition Account.cpp:3362
gint gnc_account_get_tree_depth(const Account *account)
Return the number of levels of descendants accounts below the specified account.
Definition Account.cpp:3029
int xaccAccountOrder(const Account *aa, const Account *ab)
The xaccAccountOrder() subroutine defines a sorting order on accounts.
Definition Account.cpp:2375
Account * gnc_account_create_root(QofBook *book)
Create a new root level account.
Definition Account.cpp:1284
#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:4551
void xaccAccountSetDescription(Account *acc, const char *str)
Set the account's description.
Definition Account.cpp:2497
void xaccAccountSortSplits(Account *acc, gboolean force)
The xaccAccountSortSplits() routine will resort the account's splits if the sort is dirty.
Definition Account.cpp:2004
gboolean gnc_account_remove_split(Account *acc, Split *s)
Remove the given split from an account.
Definition Account.cpp:1973
void xaccAccountDestroy(Account *acc)
The xaccAccountDestroy() routine can be used to get rid of an account.
Definition Account.cpp:1590
gboolean xaccAccountGetReconcileChildrenStatus(const Account *acc)
DOCUMENT ME!
Definition Account.cpp:4895
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:4953
void xaccAccountSetSortReversed(Account *acc, gboolean sortreversed)
Set the account's Sort Order direction.
Definition Account.cpp:2631
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:1884
void gnc_account_set_defer_bal_computation(Account *acc, gboolean defer)
Set the defer balance flag.
Definition Account.cpp:1911
void xaccAccountSetLastNum(Account *acc, const char *num)
Set the last num field of an Account.
Definition Account.cpp:4692
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:3454
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:1301
gnc_numeric xaccAccountGetBalance(const Account *acc)
Get the current balance of the account, which may include future splits.
Definition Account.cpp:3467
const char * xaccAccountGetLastNum(const Account *acc)
Get the last num field of an Account.
Definition Account.cpp:4683
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:1167
Account * gnc_account_get_parent(const Account *acc)
This routine returns a pointer to the parent of the specified account.
Definition Account.cpp:2935
void xaccAccountSetNotes(Account *acc, const char *str)
Set the account's notes.
Definition Account.cpp:2655
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:2898
void xaccAccountSetReconcileChildrenStatus(Account *acc, gboolean status)
DOCUMENT ME!
Definition Account.cpp:4882
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:3244
void xaccAccountSetType(Account *acc, GNCAccountType tip)
Set the account's type.
Definition Account.cpp:2437
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:4847
gboolean xaccAccountEqual(const Account *aa, const Account *ab, gboolean check_guids)
Compare two accounts for equality - this is a deep compare.
Definition Account.cpp:1666
gboolean xaccAccountHasStockSplit(const Account *acc)
Returns true if the account has a stock split, otherwise false.
Definition Account.cpp:3507
GNCPolicy * gnc_account_get_policy(Account *acc)
Get the account's lot order policy.
Definition Account.cpp:2100
Account * xaccMallocAccount(QofBook *book)
Constructor.
Definition Account.cpp:1270
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:3441
void xaccAccountSetAssociatedAccount(Account *acc, const char *tag, const Account *assoc_acct)
Set the account's associated account e.g.
Definition Account.cpp:2669
const char * xaccAccountGetFilter(const Account *acc)
Get the account's filter.
Definition Account.cpp:3356
void xaccAccountSetCode(Account *acc, const char *str)
Set the account's accounting code.
Definition Account.cpp:2478
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:3225
const char * xaccAccountGetOnlineID(const Account *acc)
Get the account's online_id (see xaccAccountSetOnlineID).
Definition Account.cpp:3380
const char * xaccAccountGetNotes(const Account *acc)
Get the account's notes.
Definition Account.cpp:3374
void xaccAccountSetSortOrder(Account *acc, const char *str)
Set the account's Sort Order.
Definition Account.cpp:2625
Account * xaccAccountGetAssociatedAccount(const Account *acc, const char *tag)
Get the account's associated account e.g.
Definition Account.cpp:3387
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:3429
gnc_numeric xaccAccountGetClearedBalance(const Account *acc)
Get the current balance of the account, only including cleared transactions.
Definition Account.cpp:3474
gnc_numeric xaccAccountGetReconciledBalance(const Account *acc)
Get the current balance of the account, only including reconciled transactions.
Definition Account.cpp:3481
gboolean xaccAccountGetNonStdSCU(const Account *acc)
Return boolean, indicating whether this account uses a non-standard SCU.
Definition Account.cpp:2774
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:2969
gint gnc_account_n_descendants(const Account *account)
Return the number of descendants of the specified account.
Definition Account.cpp:3002
gint gnc_account_n_children(const Account *account)
Return the number of children of the specified account.
Definition Account.cpp:2976
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:3861
const char * xaccAccountGetCode(const Account *acc)
Get the account's accounting code.
Definition Account.cpp:3336
gboolean xaccAccountGetSortReversed(const Account *acc)
Get the account's Sort Order direction.
Definition Account.cpp:3368
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account's commodity
Definition Account.cpp:3408
void xaccAccountSetCommodity(Account *acc, gnc_commodity *com)
Set the account's commodity.
Definition Account.cpp:2678
Account * xaccAccountLookup(const GncGUID *guid, QofBook *book)
The xaccAccountLookup() subroutine will return the account associated with the given id,...
Definition Account.cpp:2050
@ 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:123
void(* QofSetterFunc)(gpointer, gpointer)
The QofSetterFunc defines an function pointer for parameter setters.
Definition qofclass.h:130
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:168
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:3975
gboolean xaccAccountGetHidden(const Account *acc)
Get the "hidden" flag for an account.
Definition Account.cpp:4190
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:4103
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:4078
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:4284
gboolean xaccAccountGetPlaceholder(const Account *acc)
Get the "placeholder" flag for an account.
Definition Account.cpp:4119
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:4162
const char * xaccAccountTypeEnumAsString(GNCAccountType type)
Conversion routines for the account types to/from strings that are used in persistent storage,...
Definition Account.cpp:4247
gboolean xaccAccountGetAutoInterest(const Account *acc)
Get the "auto interest" flag for an account.
Definition Account.cpp:4175
gboolean xaccAccountGetReconcilePostponeDate(const Account *acc, time64 *postpone_date)
DOCUMENT ME!
Definition Account.cpp:4624
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:4454
GNCAccountType xaccAccountTypeGetFundamental(GNCAccountType t)
Convenience function to return the fundamental type asset/liability/income/expense/equity given an ac...
Definition Account.cpp:4495
gboolean xaccAccountGetLowerBalanceLimit(const Account *acc, gnc_numeric *balance)
Get the lower balance limit for the account.
Definition Account.cpp:4726
const char * gnc_account_get_debit_string(GNCAccountType acct_type)
Get the debit string associated with this account type.
Definition Account.cpp:4091
gboolean xaccAccountGetIsOpeningBalance(const Account *acc)
Get the "opening-balance" flag for an account.
Definition Account.cpp:4143
void xaccAccountSetAppendText(Account *acc, gboolean val)
Set the "import-append-text" flag for an account.
Definition Account.cpp:4137
void xaccAccountSetReconcileLastInterval(Account *acc, int months, int days)
DOCUMENT ME!
Definition Account.cpp:4614
void xaccAccountSetIsOpeningBalance(Account *acc, gboolean val)
Set the "opening-balance" flag for an account.
Definition Account.cpp:4153
gboolean xaccAccountGetAppendText(const Account *acc)
Get the "import-append-text" flag for an account.
Definition Account.cpp:4131
void xaccAccountSetIncludeSubAccountBalances(Account *acc, gboolean inc_sub)
Set whether to include balances of sub accounts.
Definition Account.cpp:4763
void xaccClearMarkDown(Account *acc, short val)
The xaccClearMarkDown will clear the mark only in this and in sub-accounts.
Definition Account.cpp:2084
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:5732
void gnc_account_delete_all_bayes_maps(Account *acc)
Delete all bayes entries for Account.
Definition Account.cpp:5775
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:2785
guint32 xaccAccountTypesCompatibleWith(GNCAccountType type)
Return the bitmask of account types compatible with a given type.
Definition Account.cpp:4367
gboolean xaccAccountIsHidden(const Account *acc)
Should this account be "hidden".
Definition Account.cpp:4202
Account * gnc_account_lookup_by_opening_balance(Account *account, gnc_commodity *commodity)
Find the opening balance account for the currency.
Definition Account.cpp:3121
void xaccAccountSetReconcileLastDate(Account *acc, time64 last_date)
DOCUMENT ME!
Definition Account.cpp:4584
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:4071
void xaccAccountSetTaxUSCode(Account *acc, const char *code)
DOCUMENT ME!
Definition Account.cpp:4053
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:3093
void xaccAccountSetHigherBalanceLimit(Account *acc, gnc_numeric balance)
Set the higher balance limit for the account.
Definition Account.cpp:4733
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:5089
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:3188
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:5703
GNCAccountType xaccAccountStringToEnum(const char *str)
Conversion routines for the account types to/from strings that are used in persistent storage,...
Definition Account.cpp:4318
gboolean xaccAccountIsAPARType(GNCAccountType t)
Convenience function to check if the account is a valid business account type (meaning an Accounts Pa...
Definition Account.cpp:4527
GNCPlaceholderType
DOCUMENT ME!
Definition Account.h:1283
gboolean xaccAccountGetTaxRelated(const Account *acc)
DOCUMENT ME!
Definition Account.cpp:4035
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:4639
void xaccAccountSetAutoInterest(Account *acc, gboolean val)
Set the "auto interest" flag for an account.
Definition Account.cpp:4181
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:5054
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:3982
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:4481
void xaccAccountInsertLot(Account *acc, GNCLot *lot)
The xaccAccountInsertLot() method will register the indicated lot with this account.
Definition Account.cpp:2140
gboolean xaccAccountGetReconcileLastInterval(const Account *acc, int *months, int *days)
DOCUMENT ME!
Definition Account.cpp:4593
void xaccAccountSetMark(Account *acc, short m)
Set a mark on the account.
Definition Account.cpp:2062
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:4860
guint32 xaccAccountTypesValid(void)
Returns the bitmask of the account type enums that are valid.
Definition Account.cpp:4472
Split * xaccAccountFindSplitByDesc(const Account *acc, const char *description)
Returns a pointer to the split, not a copy.
Definition Account.cpp:4908
void gnc_account_imap_info_destroy(GncImapInfo *imapInfo)
Clean destructor for the imap_info structure of Bayesian mappings.
Definition Account.cpp:5682
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:5127
gboolean xaccAccountGetHigherBalanceLimit(const Account *acc, gnc_numeric *balance)
Get the higher balance limit for the account.
Definition Account.cpp:4719
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:5748
const char * xaccAccountGetTaxUSCode(const Account *acc)
DOCUMENT ME!
Definition Account.cpp:4047
void xaccAccountSetReconcilePostponeBalance(Account *acc, gnc_numeric balance)
DOCUMENT ME!
Definition Account.cpp:4664
const char * xaccAccountGetTaxUSPayerNameSource(const Account *acc)
DOCUMENT ME!
Definition Account.cpp:4059
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:5493
gboolean xaccAccountGetReconcilePostponeBalance(const Account *acc, gnc_numeric *balance)
DOCUMENT ME!
Definition Account.cpp:4648
void xaccAccountMoveAllSplits(Account *accfrom, Account *accto)
The xaccAccountMoveAllSplits() routine reassigns each of the splits in accfrom to accto.
Definition Account.cpp:2196
void xaccAccountSetTaxRelated(Account *acc, gboolean tax_related)
DOCUMENT ME!
Definition Account.cpp:4041
gboolean xaccAccountGetReconcileLastDate(const Account *acc, time64 *last_date)
DOCUMENT ME!
Definition Account.cpp:4566
void xaccAccountClearLowerBalanceLimit(Account *acc)
Clear the lower balance limit for the account.
Definition Account.cpp:4751
void xaccAccountSetLowerBalanceLimit(Account *acc, gnc_numeric balance)
Set the lower balance limit for the account.
Definition Account.cpp:4739
Account * gnc_account_imap_find_account_bayes(Account *acc, GList *tokens)
Look up an Account in the map.
Definition Account.cpp:5525
void xaccAccountClearReconcilePostpone(Account *acc)
DOCUMENT ME!
Definition Account.cpp:4674
void xaccAccountClearHigherBalanceLimit(Account *acc)
Clear the higher balance limit for the account.
Definition Account.cpp:4745
void xaccAccountBeginStagedTransactionTraversals(const Account *account)
xaccAccountBeginStagedTransactionTraversals() resets the traversal marker for each transaction which ...
Definition Account.cpp:5031
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:5571
guint32 xaccParentAccountTypesCompatibleWith(GNCAccountType type)
Return the bitmask of parent account types compatible with a given type.
Definition Account.cpp:4407
void xaccAccountSetHidden(Account *acc, gboolean val)
Set the "hidden" flag for an account.
Definition Account.cpp:4196
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:5062
Transaction * xaccAccountFindTransByDesc(const Account *acc, const char *description)
Returns a pointer to the transaction, not a copy.
Definition Account.cpp:4921
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:5692
gboolean xaccTransactionTraverse(Transaction *trans, int stage)
xaccTransactionTraverse() checks the stage of the given transaction.
Definition Account.cpp:5039
gboolean xaccAccountGetIncludeSubAccountBalances(const Account *acc)
Get whether to include balances of sub accounts.
Definition Account.cpp:4757
void xaccAccountSetPlaceholder(Account *acc, gboolean val)
Set the "placeholder" flag for an account.
Definition Account.cpp:4125
gboolean xaccAccountIsEquityType(GNCAccountType t)
Convenience function to check if the account is a valid Equity type.
Definition Account.cpp:4539
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:3163
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:3949
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:3106
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:5740
void xaccClearMark(Account *acc, short val)
Get the mark set by xaccAccountSetMark short xaccAccountGetMark (const Account *account);.
Definition Account.cpp:2073
void xaccAccountSetTaxUSPayerNameSource(Account *acc, const char *source)
DOCUMENT ME!
Definition Account.cpp:4065
time64 xaccTransGetDate(const Transaction *trans)
Retrieve the posted date of the transaction.
gnc_commodity * DxaccAccountGetCurrency(const Account *acc)
Definition Account.cpp:3396
const char * xaccAccountGetTypeStr(GNCAccountType type)
The xaccAccountGetTypeStr() routine returns a string suitable for use in the GUI/Interface.
Definition Account.cpp:4357
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:4871
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:5150
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:1346
gboolean xaccSplitDestroy(Split *split)
Destructor.
Definition Split.cpp:1506
gint xaccSplitOrder(const Split *sa, const Split *sb)
The xaccSplitOrder(sa,sb) method is useful for sorting.
Definition Split.cpp:1536
gnc_numeric xaccSplitGetNoclosingBalance(const Split *s)
The noclosing-balance is the currency-denominated balance of all transactions except 'closing' transa...
Definition Split.cpp:1322
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:1334
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:1340
gnc_numeric xaccSplitGetBalance(const Split *s)
Returns the running balance up to and including the indicated split.
Definition Split.cpp:1316
#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:2078
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:5273
intermediate values used to calculate the bayes probability of a given account where p(AB) = (a*b)/[a...
Definition Account.cpp:5249
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:5264