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