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