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