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