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