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