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