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 *
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 static GncGUID*
2587 get_kvp_guid_path (const Account *acc, const Path& path)
2588 {
2589  auto val{qof_instance_get_path_kvp<GncGUID*> (QOF_INSTANCE(acc), path)};
2590  return val ? guid_copy(*val) : nullptr;
2591 }
2592 
2593 void
2594 xaccAccountSetColor (Account *acc, const char *str)
2595 {
2596  set_kvp_string_path (acc, {"color"}, str);
2597 }
2598 
2599 void
2600 xaccAccountSetFilter (Account *acc, const char *str)
2601 {
2602  set_kvp_string_path (acc, {"filter"}, str);
2603 }
2604 
2605 void
2606 xaccAccountSetSortOrder (Account *acc, const char *str)
2607 {
2608  set_kvp_string_path (acc, {"sort-order"}, str);
2609 }
2610 
2611 void
2612 xaccAccountSetSortReversed (Account *acc, gboolean sortreversed)
2613 {
2614  set_kvp_boolean_path (acc, {"sort-reversed"}, sortreversed);
2615 }
2616 
2617 static void
2618 qofAccountSetParent (Account *acc, QofInstance *parent)
2619 {
2620  Account *parent_acc;
2621 
2622  g_return_if_fail(GNC_IS_ACCOUNT(acc));
2623  g_return_if_fail(GNC_IS_ACCOUNT(parent));
2624 
2625  parent_acc = GNC_ACCOUNT(parent);
2626  xaccAccountBeginEdit(acc);
2627  xaccAccountBeginEdit(parent_acc);
2628  gnc_account_append_child(parent_acc, acc);
2629  mark_account (parent_acc);
2630  mark_account (acc);
2631  xaccAccountCommitEdit(acc);
2632  xaccAccountCommitEdit(parent_acc);
2633 }
2634 
2635 void
2636 xaccAccountSetNotes (Account *acc, const char *str)
2637 {
2638  set_kvp_string_path (acc, {"notes"}, str);
2639 }
2640 
2641 void
2642 xaccAccountSetOnlineID (Account *acc, const char *id)
2643 {
2644  g_return_if_fail (GNC_IS_ACCOUNT(acc));
2645  set_kvp_string_path (acc, {KEY_ONLINE_ID}, id);
2646 }
2647 
2648 
2649 void
2650 xaccAccountSetAssociatedAccount (Account *acc, const char *tag, const Account* assoc_acct)
2651 {
2652  g_return_if_fail (GNC_IS_ACCOUNT(acc));
2653  g_return_if_fail (tag && *tag);
2654 
2655  set_kvp_account_path (acc, {"associated-account", tag}, assoc_acct);
2656 }
2657 
2658 void
2659 xaccAccountSetCommodity (Account * acc, gnc_commodity * com)
2660 {
2661  AccountPrivate *priv;
2662 
2663  /* errors */
2664  g_return_if_fail(GNC_IS_ACCOUNT(acc));
2665  g_return_if_fail(GNC_IS_COMMODITY(com));
2666 
2667  /* optimizations */
2668  priv = GET_PRIVATE(acc);
2669  if (com == priv->commodity)
2670  return;
2671 
2672  xaccAccountBeginEdit(acc);
2673  gnc_commodity_decrement_usage_count(priv->commodity);
2674  priv->commodity = com;
2676  priv->commodity_scu = gnc_commodity_get_fraction(com);
2677  priv->non_standard_scu = FALSE;
2678 
2679  /* iterate over splits */
2680  for (auto s : priv->splits)
2681  {
2682  Transaction *trans = xaccSplitGetParent (s);
2683 
2684  xaccTransBeginEdit (trans);
2686  xaccTransCommitEdit (trans);
2687  }
2688 
2689  priv->sort_dirty = TRUE; /* Not needed. */
2690  priv->balance_dirty = TRUE;
2691  mark_account (acc);
2692 
2693  xaccAccountCommitEdit(acc);
2694 }
2695 
2696 /*
2697  * Set the account scu and then check to see if it is the same as the
2698  * commodity scu. This function is called when parsing the data file
2699  * and is designed to catch cases where the two were accidentally set
2700  * to mismatched values in the past.
2701  */
2702 void
2704 {
2705  AccountPrivate *priv;
2706 
2707  g_return_if_fail(GNC_IS_ACCOUNT(acc));
2708 
2709  priv = GET_PRIVATE(acc);
2710  xaccAccountBeginEdit(acc);
2711  priv->commodity_scu = scu;
2712  if (scu != gnc_commodity_get_fraction(priv->commodity))
2713  priv->non_standard_scu = TRUE;
2714  mark_account(acc);
2715  xaccAccountCommitEdit(acc);
2716 }
2717 
2718 int
2720 {
2721  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), 0);
2722  return GET_PRIVATE(acc)->commodity_scu;
2723 }
2724 
2725 int
2727 {
2728  AccountPrivate *priv;
2729 
2730  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), 0);
2731 
2732  priv = GET_PRIVATE(acc);
2733  if (priv->non_standard_scu || !priv->commodity)
2734  return priv->commodity_scu;
2735  return gnc_commodity_get_fraction(priv->commodity);
2736 }
2737 
2738 void
2739 xaccAccountSetNonStdSCU (Account *acc, gboolean flag)
2740 {
2741  AccountPrivate *priv;
2742 
2743  g_return_if_fail(GNC_IS_ACCOUNT(acc));
2744 
2745  priv = GET_PRIVATE(acc);
2746  if (priv->non_standard_scu == flag)
2747  return;
2748  xaccAccountBeginEdit(acc);
2749  priv->non_standard_scu = flag;
2750  mark_account (acc);
2751  xaccAccountCommitEdit(acc);
2752 }
2753 
2754 gboolean
2756 {
2757  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), 0);
2758  return GET_PRIVATE(acc)->non_standard_scu;
2759 }
2760 
2761 /********************************************************************\
2762 \********************************************************************/
2763 /* below follow the old, deprecated currency/security routines. */
2764 
2765 void
2766 DxaccAccountSetCurrency (Account * acc, gnc_commodity * currency)
2767 {
2768  if ((!acc) || (!currency)) return;
2769 
2770  auto s = gnc_commodity_get_unique_name (currency);
2771  set_kvp_string_path (acc, {"old-currency"}, s);
2772 
2773  auto book = qof_instance_get_book(acc);
2774  auto table = gnc_commodity_table_get_table (book);
2775  auto commodity = gnc_commodity_table_lookup_unique (table, s);
2776 
2777  if (!commodity)
2778  gnc_commodity_table_insert (table, currency);
2779 }
2780 
2781 /********************************************************************\
2782 \********************************************************************/
2783 
2784 void
2785 gnc_account_foreach_descendant (const Account *acc, std::function<void(Account*)> account_cb)
2786 {
2787  g_return_if_fail (GNC_IS_ACCOUNT(acc));
2788 
2789  // children must be a vector copy instead of reference because
2790  // some callers e.g. xaccAccountTreeScrubLots will modify the
2791  // children
2792  auto children = GET_PRIVATE(acc)->children;
2793  for (auto child : children)
2794  {
2795  account_cb (child);
2796  gnc_account_foreach_descendant (child, account_cb);
2797  }
2798 }
2799 
2800 static void
2801 account_foreach_descendant_sorted (const Account *acc, std::function<void(Account*)> account_cb)
2802 {
2803  g_return_if_fail (GNC_IS_ACCOUNT(acc));
2804 
2805  auto children = GET_PRIVATE(acc)->children;
2806  std::sort (children.begin(), children.end(),
2807  [](auto a, auto b) { return xaccAccountOrder (a, b) < 0; });
2808 
2809  for (auto child : children)
2810  {
2811  account_cb (child);
2812  account_foreach_descendant_sorted (child, account_cb);
2813  }
2814 }
2815 
2816 void
2818 {
2819  AccountPrivate *ppriv, *cpriv;
2820  Account *old_parent;
2821  QofCollection *col;
2822 
2823  /* errors */
2824  g_assert(GNC_IS_ACCOUNT(new_parent));
2825  g_assert(GNC_IS_ACCOUNT(child));
2826 
2827  /* optimizations */
2828  ppriv = GET_PRIVATE(new_parent);
2829  cpriv = GET_PRIVATE(child);
2830  old_parent = cpriv->parent;
2831  if (old_parent == new_parent)
2832  return;
2833 
2834  // xaccAccountBeginEdit(new_parent);
2835  xaccAccountBeginEdit(child);
2836  if (old_parent)
2837  {
2838  gnc_account_remove_child(old_parent, child);
2839 
2840  if (!qof_instance_books_equal(old_parent, new_parent))
2841  {
2842  /* hack alert -- this implementation is not exactly correct.
2843  * If the entity tables are not identical, then the 'from' book
2844  * may have a different backend than the 'to' book. This means
2845  * that we should get the 'from' backend to destroy this account,
2846  * and the 'to' backend to save it. Right now, this is broken.
2847  *
2848  * A 'correct' implementation similar to this is in Period.c
2849  * except its for transactions ...
2850  *
2851  * Note also, we need to reparent the children to the new book as well.
2852  */
2853  PWARN ("reparenting accounts across books is not correctly supported\n");
2854 
2855  qof_event_gen (&child->inst, QOF_EVENT_DESTROY, nullptr);
2857  GNC_ID_ACCOUNT);
2858  qof_collection_insert_entity (col, &child->inst);
2859  qof_event_gen (&child->inst, QOF_EVENT_CREATE, nullptr);
2860  }
2861  }
2862  cpriv->parent = new_parent;
2863  ppriv->children.push_back (child);
2864  qof_instance_set_dirty(&new_parent->inst);
2865  qof_instance_set_dirty(&child->inst);
2866 
2867  /* Send events data. Warning: The call to commit_edit is also going
2868  * to send a MODIFY event. If the gtktreemodelfilter code gets the
2869  * MODIFY before it gets the ADD, it gets very confused and thinks
2870  * that two nodes have been added. */
2871  qof_event_gen (&child->inst, QOF_EVENT_ADD, nullptr);
2872  // qof_event_gen (&new_parent->inst, QOF_EVENT_MODIFY, nullptr);
2873 
2874  xaccAccountCommitEdit (child);
2875  // xaccAccountCommitEdit(new_parent);
2876 }
2877 
2878 void
2880 {
2881  AccountPrivate *ppriv, *cpriv;
2882  GncEventData ed;
2883 
2884  if (!child) return;
2885 
2886  /* Note this routine might be called on accounts which
2887  * are not yet parented. */
2888  if (!parent) return;
2889 
2890  ppriv = GET_PRIVATE(parent);
2891  cpriv = GET_PRIVATE(child);
2892 
2893  if (cpriv->parent != parent)
2894  {
2895  PERR ("account not a child of parent");
2896  return;
2897  }
2898 
2899  /* Gather event data */
2900  ed.node = parent;
2901  ed.idx = gnc_account_child_index (parent, child);
2902 
2903  ppriv->children.erase (std::remove (ppriv->children.begin(), ppriv->children.end(), child),
2904  ppriv->children.end());
2905 
2906  /* Now send the event. */
2907  qof_event_gen(&child->inst, QOF_EVENT_REMOVE, &ed);
2908 
2909  /* clear the account's parent pointer after REMOVE event generation. */
2910  cpriv->parent = nullptr;
2911 
2912  qof_event_gen (&parent->inst, QOF_EVENT_MODIFY, nullptr);
2913 }
2914 
2915 Account *
2917 {
2918  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
2919  return GET_PRIVATE(acc)->parent;
2920 }
2921 
2922 Account *
2924 {
2925  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
2926 
2927  while (auto parent = gnc_account_get_parent (acc))
2928  acc = parent;
2929 
2930  return acc;
2931 }
2932 
2933 gboolean
2935 {
2936  g_return_val_if_fail(GNC_IS_ACCOUNT(account), FALSE);
2937  return (GET_PRIVATE(account)->parent == nullptr);
2938 }
2939 
2940 GList *
2942 {
2943  g_return_val_if_fail(GNC_IS_ACCOUNT(account), nullptr);
2944  auto& children = GET_PRIVATE(account)->children;
2945  return std::accumulate (children.rbegin(), children.rend(), static_cast<GList*>(nullptr),
2946  g_list_prepend);
2947 }
2948 
2949 GList *
2951 {
2952  g_return_val_if_fail(GNC_IS_ACCOUNT(account), nullptr);
2953  return g_list_sort(gnc_account_get_children (account), (GCompareFunc)xaccAccountOrder);
2954 }
2955 
2956 gint
2958 {
2959  g_return_val_if_fail(GNC_IS_ACCOUNT(account), 0);
2960  return GET_PRIVATE(account)->children.size();
2961 }
2962 
2963 gint
2964 gnc_account_child_index (const Account *parent, const Account *child)
2965 {
2966  g_return_val_if_fail(GNC_IS_ACCOUNT(parent), -1);
2967  g_return_val_if_fail(GNC_IS_ACCOUNT(child), -1);
2968  auto& children = GET_PRIVATE(parent)->children;
2969  auto find_it = std::find (children.begin(), children.end(), child);
2970  return find_it == children.end() ? -1 : std::distance (children.begin(), find_it);
2971 }
2972 
2973 Account *
2974 gnc_account_nth_child (const Account *parent, gint num)
2975 {
2976  g_return_val_if_fail(GNC_IS_ACCOUNT(parent), nullptr);
2977  if ((size_t)num >= GET_PRIVATE(parent)->children.size())
2978  return nullptr;
2979  return static_cast<Account*>(GET_PRIVATE(parent)->children.at (num));
2980 }
2981 
2982 gint
2984 {
2985  int count {0};
2986  gnc_account_foreach_descendant (account, [&count](auto acc){ count++; });
2987  return count;
2988 }
2989 
2990 gint
2992 {
2993  AccountPrivate *priv;
2994  int depth = 0;
2995 
2996  g_return_val_if_fail(GNC_IS_ACCOUNT(account), 0);
2997 
2998  priv = GET_PRIVATE(account);
2999  while (priv->parent && (priv->type != ACCT_TYPE_ROOT))
3000  {
3001  account = priv->parent;
3002  priv = GET_PRIVATE(account);
3003  depth++;
3004  }
3005 
3006  return depth;
3007 }
3008 
3009 gint
3011 {
3012  AccountPrivate *priv;
3013  g_return_val_if_fail(GNC_IS_ACCOUNT(account), 0);
3014 
3015  priv = GET_PRIVATE(account);
3016  if (!priv->children.size())
3017  return 1;
3018 
3019  return 1 + std::accumulate (priv->children.begin(), priv->children.end(),
3020  0, [](auto a, auto b)
3021  { return std::max (a, gnc_account_get_tree_depth (b)); });
3022 }
3023 
3024 GList *
3026 {
3027  GList* list = nullptr;
3028  gnc_account_foreach_descendant (account, [&list](auto a){ list = g_list_prepend (list, a); });
3029  return g_list_reverse (list);
3030 }
3031 
3032 GList *
3034 {
3035  GList* list = nullptr;
3036  account_foreach_descendant_sorted (account, [&list](auto a){ list = g_list_prepend (list, a); });
3037  return g_list_reverse (list);
3038 }
3039 
3040 // because gnc_account_lookup_by_name and gnc_account_lookup_by_code
3041 // are described in Account.h searching breadth-first until 4.6, and
3042 // accidentally modified to search depth-first from 4.7
3043 // onwards. Restore breath-first searching in 4.11 onwards to match
3044 // previous behaviour and function description in Account.h
3045 static gpointer
3046 account_foreach_descendant_breadthfirst_until (const Account *acc,
3047  AccountCb2 thunk,
3048  gpointer user_data)
3049 {
3050  g_return_val_if_fail (GNC_IS_ACCOUNT(acc), nullptr);
3051  g_return_val_if_fail (thunk, nullptr);
3052 
3053  auto& children{GET_PRIVATE(acc)->children};
3054 
3055  for (auto acc : children)
3056  if (auto result = thunk (acc, user_data))
3057  return result;
3058 
3059  for (auto acc: children)
3060  if (auto result = account_foreach_descendant_breadthfirst_until (acc, thunk, user_data))
3061  return result;
3062 
3063  return nullptr;
3064 }
3065 
3066 static gpointer
3067 is_acct_name (Account *account, gpointer user_data)
3068 {
3069  auto name {static_cast<gchar*>(user_data)};
3070  return (g_strcmp0 (name, xaccAccountGetName (account)) ? nullptr : account);
3071 }
3072 
3073 Account *
3074 gnc_account_lookup_by_name (const Account *parent, const char * name)
3075 {
3076  return (Account*)account_foreach_descendant_breadthfirst_until (parent, is_acct_name, (char*)name);
3077 }
3078 
3079 static gpointer
3080 is_acct_code (Account *account, gpointer user_data)
3081 {
3082  auto name {static_cast<gchar*>(user_data)};
3083  return (g_strcmp0 (name, xaccAccountGetCode (account)) ? nullptr : account);
3084 }
3085 
3086 Account *
3087 gnc_account_lookup_by_code (const Account *parent, const char * code)
3088 {
3089  return (Account*)account_foreach_descendant_breadthfirst_until (parent, is_acct_code, (char*)code);
3090 }
3091 
3092 static gpointer
3093 is_opening_balance_account (Account* account, gpointer data)
3094 {
3095  gnc_commodity* commodity = GNC_COMMODITY(data);
3096  if (xaccAccountGetIsOpeningBalance(account) && gnc_commodity_equiv(commodity, xaccAccountGetCommodity(account)))
3097  return account;
3098  return nullptr;
3099 }
3100 
3101 Account*
3102 gnc_account_lookup_by_opening_balance (Account* account, gnc_commodity* commodity)
3103 {
3104  return (Account *)gnc_account_foreach_descendant_until (account, is_opening_balance_account, commodity);
3105 }
3106 
3107 /********************************************************************\
3108  * Fetch an account, given its full name *
3109 \********************************************************************/
3110 
3111 static Account *
3112 gnc_account_lookup_by_full_name_helper (const Account *parent,
3113  gchar **names)
3114 {
3115  g_return_val_if_fail(GNC_IS_ACCOUNT(parent), nullptr);
3116  g_return_val_if_fail(names, nullptr);
3117 
3118  /* Look for the first name in the children. */
3119  for (auto account : GET_PRIVATE(parent)->children)
3120  {
3121  auto priv = GET_PRIVATE(account);
3122  if (g_strcmp0(priv->accountName, names[0]) == 0)
3123  {
3124  /* We found an account. If the next entry is nullptr, there is
3125  * nothing left in the name, so just return the account. */
3126  if (names[1] == nullptr)
3127  return account;
3128 
3129  /* No children? We're done. */
3130  if (priv->children.empty())
3131  return nullptr;
3132 
3133  /* There's stuff left to search for. Search recursively. */
3134  if (auto found = gnc_account_lookup_by_full_name_helper(account, &names[1]))
3135  return found;
3136  }
3137  }
3138 
3139  return nullptr;
3140 }
3141 
3142 
3143 Account *
3145  const gchar *name)
3146 {
3147  const AccountPrivate *rpriv;
3148  const Account *root;
3149  Account *found;
3150  gchar **names;
3151 
3152  g_return_val_if_fail(GNC_IS_ACCOUNT(any_acc), nullptr);
3153  g_return_val_if_fail(name, nullptr);
3154 
3155  root = any_acc;
3156  rpriv = GET_PRIVATE(root);
3157  while (rpriv->parent)
3158  {
3159  root = rpriv->parent;
3160  rpriv = GET_PRIVATE(root);
3161  }
3162  names = g_strsplit(name, gnc_get_account_separator_string(), -1);
3163  found = gnc_account_lookup_by_full_name_helper(root, names);
3164  g_strfreev(names);
3165  return found;
3166 }
3167 
3168 GList*
3170  const char* name,
3171  GNCAccountType acctype,
3172  gnc_commodity* commodity)
3173 {
3174  GList *retval{};
3175  auto rpriv{GET_PRIVATE(root)};
3176  for (auto account : rpriv->children)
3177  {
3178  if (xaccAccountGetType (account) == acctype)
3179  {
3180  if (commodity &&
3182  commodity))
3183  continue;
3184 
3185  if (name && strcmp(name, xaccAccountGetName(account)))
3186  continue;
3187 
3188  retval = g_list_prepend(retval, account);
3189  }
3190  }
3191 
3192  if (!retval) // Recurse through the children
3193  for (auto account : rpriv->children)
3194  {
3195  auto result = gnc_account_lookup_by_type_and_commodity(account,
3196  name,
3197  acctype,
3198  commodity);
3199  if (result)
3200  retval = g_list_concat(result, retval);
3201  }
3202  return retval;
3203 }
3204 
3205 void
3207  AccountCb thunk,
3208  gpointer user_data)
3209 {
3210  g_return_if_fail(GNC_IS_ACCOUNT(acc));
3211  g_return_if_fail(thunk);
3212  std::for_each (GET_PRIVATE(acc)->children.begin(), GET_PRIVATE(acc)->children.end(),
3213  [user_data, thunk](auto a){ thunk (a, user_data); });
3214 }
3215 
3216 void
3218  AccountCb thunk,
3219  gpointer user_data)
3220 {
3221  gnc_account_foreach_descendant (acc, [&](auto acc){ thunk (acc, user_data); });
3222 }
3223 
3224 gpointer
3226  AccountCb2 thunk,
3227  gpointer user_data)
3228 {
3229  gpointer result {nullptr};
3230 
3231  g_return_val_if_fail (GNC_IS_ACCOUNT(acc), nullptr);
3232  g_return_val_if_fail (thunk, nullptr);
3233 
3234  for (auto child : GET_PRIVATE(acc)->children)
3235  {
3236  result = thunk (child, user_data);
3237  if (result) break;
3238 
3239  result = gnc_account_foreach_descendant_until (child, thunk, user_data);
3240  if (result) break;
3241  }
3242 
3243  return result;
3244 }
3245 
3246 
3249 {
3250  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), ACCT_TYPE_NONE);
3251  return GET_PRIVATE(acc)->type;
3252 }
3253 
3254 static const char*
3255 qofAccountGetTypeString (const Account *acc)
3256 {
3257  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
3258  return xaccAccountTypeEnumAsString(GET_PRIVATE(acc)->type);
3259 }
3260 
3261 static void
3262 qofAccountSetType (Account *acc, const char *type_string)
3263 {
3264  g_return_if_fail(GNC_IS_ACCOUNT(acc));
3265  g_return_if_fail(type_string);
3266  xaccAccountSetType(acc, xaccAccountStringToEnum(type_string));
3267 }
3268 
3269 const char *
3271 {
3272  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
3273  return GET_PRIVATE(acc)->accountName;
3274 }
3275 
3276 std::vector<const Account*>
3277 gnc_account_get_all_parents (const Account *account)
3278 {
3279  std::vector<const Account*> rv;
3280  for (auto a = account; !gnc_account_is_root (a); a = gnc_account_get_parent (a))
3281  rv.push_back (a);
3282  return rv;
3283 }
3284 
3285 gchar *
3287 {
3288  /* So much for hardening the API. Too many callers to this function don't
3289  * bother to check if they have a non-nullptr pointer before calling. */
3290  if (nullptr == account)
3291  return g_strdup("");
3292 
3293  /* errors */
3294  g_return_val_if_fail(GNC_IS_ACCOUNT(account), g_strdup(""));
3295 
3296  auto path{gnc_account_get_all_parents (account)};
3297  auto seps_size{path.empty() ? 0 : strlen (account_separator) * (path.size() - 1)};
3298  auto alloc_size{std::accumulate (path.begin(), path.end(), seps_size,
3299  [](auto sum, auto acc)
3300  { return sum + strlen (xaccAccountGetName (acc)); })};
3301  auto rv = g_new (char, alloc_size + 1);
3302  auto p = rv;
3303 
3304  std::for_each (path.rbegin(), path.rend(),
3305  [&p, rv](auto a)
3306  {
3307  if (p != rv)
3308  p = stpcpy (p, account_separator);
3309  p = stpcpy (p, xaccAccountGetName (a));
3310  });
3311  *p = '\0';
3312 
3313  return rv;
3314 }
3315 
3316 const char *
3318 {
3319  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
3320  return GET_PRIVATE(acc)->accountCode;
3321 }
3322 
3323 const char *
3325 {
3326  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
3327  return GET_PRIVATE(acc)->description;
3328 }
3329 
3330 const char *
3332 {
3333  return get_kvp_string_path (acc, {"color"});
3334 }
3335 
3336 const char *
3338 {
3339  return get_kvp_string_path (acc, {"filter"});
3340 }
3341 
3342 const char *
3344 {
3345  return get_kvp_string_path (acc, {"sort-order"});
3346 }
3347 
3348 gboolean
3350 {
3351  return get_kvp_boolean_path (acc, {"sort-reversed"});
3352 }
3353 
3354 const char *
3356 {
3357  return get_kvp_string_path (acc, {"notes"});
3358 }
3359 
3360 const char *
3362 {
3363  g_return_val_if_fail (GNC_IS_ACCOUNT(acc), nullptr);
3364  return get_kvp_string_path (acc, {KEY_ONLINE_ID});
3365 }
3366 
3367 Account*
3368 xaccAccountGetAssociatedAccount (const Account *acc, const char *tag)
3369 {
3370  g_return_val_if_fail (tag && *tag, nullptr);
3371 
3372  return get_kvp_account_path (acc, {"associated-account", tag});
3373 }
3374 
3375 
3376 gnc_commodity *
3378 {
3379  if (auto s = get_kvp_string_path (acc, {"old-currency"}))
3380  {
3382  return gnc_commodity_table_lookup_unique (table, s);
3383  }
3384 
3385  return nullptr;
3386 }
3387 
3388 gnc_commodity *
3390 {
3391  if (!GNC_IS_ACCOUNT(acc))
3392  return nullptr;
3393  return GET_PRIVATE(acc)->commodity;
3394 }
3395 
3396 gnc_commodity * gnc_account_get_currency_or_parent(const Account* account)
3397 {
3398  g_return_val_if_fail (GNC_IS_ACCOUNT (account), nullptr);
3399 
3400  for (auto acc = account; acc; acc = gnc_account_get_parent (acc))
3401  if (auto comm = xaccAccountGetCommodity (acc); gnc_commodity_is_currency (comm))
3402  return comm;
3403 
3404  return nullptr; // no suitable commodity found.
3405 }
3406 
3407 /********************************************************************\
3408 \********************************************************************/
3409 void
3410 gnc_account_set_start_balance (Account *acc, const gnc_numeric start_baln)
3411 {
3412  AccountPrivate *priv;
3413 
3414  g_return_if_fail(GNC_IS_ACCOUNT(acc));
3415 
3416  priv = GET_PRIVATE(acc);
3417  priv->starting_balance = start_baln;
3418  priv->balance_dirty = TRUE;
3419 }
3420 
3421 void
3423  const gnc_numeric start_baln)
3424 {
3425  AccountPrivate *priv;
3426 
3427  g_return_if_fail(GNC_IS_ACCOUNT(acc));
3428 
3429  priv = GET_PRIVATE(acc);
3430  priv->starting_cleared_balance = start_baln;
3431  priv->balance_dirty = TRUE;
3432 }
3433 
3434 void
3436  const gnc_numeric start_baln)
3437 {
3438  AccountPrivate *priv;
3439 
3440  g_return_if_fail(GNC_IS_ACCOUNT(acc));
3441 
3442  priv = GET_PRIVATE(acc);
3443  priv->starting_reconciled_balance = start_baln;
3444  priv->balance_dirty = TRUE;
3445 }
3446 
3447 gnc_numeric
3449 {
3450  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
3451  return GET_PRIVATE(acc)->balance;
3452 }
3453 
3454 gnc_numeric
3456 {
3457  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
3458  return GET_PRIVATE(acc)->cleared_balance;
3459 }
3460 
3461 gnc_numeric
3463 {
3464  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
3465  return GET_PRIVATE(acc)->reconciled_balance;
3466 }
3467 
3468 gnc_numeric
3469 xaccAccountGetProjectedMinimumBalance (const Account *acc)
3470 {
3471  auto today{gnc_time64_get_today_end()};
3472  std::optional<gnc_numeric> minimum;
3473 
3474  auto before_today_end = [&minimum, today](const Split *s) -> bool
3475  {
3476  auto bal{xaccSplitGetBalance(s)};
3477  if (!minimum || gnc_numeric_compare (bal, *minimum) < 0)
3478  minimum = bal;
3479  return (xaccTransGetDate(xaccSplitGetParent(s)) < today);
3480  };
3481  // scan to find today's split, but we're really interested in the
3482  // minimum balance
3483  [[maybe_unused]] auto todays_split = gnc_account_find_split (acc, before_today_end, true);
3484  return minimum ? *minimum : gnc_numeric_zero();
3485 }
3486 
3487 
3488 /********************************************************************\
3489 \********************************************************************/
3490 
3491 static gnc_numeric
3492 GetBalanceAsOfDate (Account *acc, time64 date, std::function<gnc_numeric(Split*)> split_to_numeric)
3493 {
3494  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
3495 
3496  xaccAccountSortSplits (acc, TRUE); /* just in case, normally a noop */
3497  xaccAccountRecomputeBalance (acc); /* just in case, normally a noop */
3498 
3499  auto is_before_date = [date](auto s) -> bool
3500  { return xaccTransGetDate(xaccSplitGetParent(s)) < date; };
3501 
3502  auto latest_split{gnc_account_find_split (acc, is_before_date, true)};
3503  return latest_split ? split_to_numeric (latest_split) : gnc_numeric_zero();
3504 }
3505 
3506 gnc_numeric
3508 {
3509  return GetBalanceAsOfDate (acc, date, xaccSplitGetBalance);
3510 }
3511 
3512 static gnc_numeric
3513 xaccAccountGetNoclosingBalanceAsOfDate (Account *acc, time64 date)
3514 {
3515  return GetBalanceAsOfDate (acc, date, xaccSplitGetNoclosingBalance);
3516 }
3517 
3518 gnc_numeric
3520 {
3521  return GetBalanceAsOfDate (acc, date, xaccSplitGetReconciledBalance);
3522 }
3523 
3524 /*
3525  * Originally gsr_account_present_balance in gnc-split-reg.c
3526  */
3527 gnc_numeric
3528 xaccAccountGetPresentBalance (const Account *acc)
3529 {
3530  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
3531 
3532  return xaccAccountGetBalanceAsOfDate (GNC_ACCOUNT (acc),
3534 }
3535 
3536 
3537 /********************************************************************\
3538 \********************************************************************/
3539 /* XXX TODO: These 'GetBal' routines should be moved to some
3540  * utility area outside of the core account engine area.
3541  */
3542 
3543 /*
3544  * Convert a balance from one currency to another.
3545  */
3546 gnc_numeric
3547 xaccAccountConvertBalanceToCurrency(const Account *acc, /* for book */
3548  gnc_numeric balance,
3549  const gnc_commodity *balance_currency,
3550  const gnc_commodity *new_currency)
3551 {
3552  QofBook *book;
3553  GNCPriceDB *pdb;
3554 
3555  if (gnc_numeric_zero_p (balance) ||
3556  gnc_commodity_equiv (balance_currency, new_currency))
3557  return balance;
3558 
3559  book = gnc_account_get_book (acc);
3560  pdb = gnc_pricedb_get_db (book);
3561 
3563  pdb, balance, balance_currency, new_currency);
3564 
3565  return balance;
3566 }
3567 
3568 /*
3569  * Convert a balance from one currency to another with price of
3570  * a given date.
3571  */
3572 gnc_numeric
3573 xaccAccountConvertBalanceToCurrencyAsOfDate(const Account *acc, /* for book */
3574  gnc_numeric balance,
3575  const gnc_commodity *balance_currency,
3576  const gnc_commodity *new_currency,
3577  time64 date)
3578 {
3579  QofBook *book;
3580  GNCPriceDB *pdb;
3581 
3582  if (gnc_numeric_zero_p (balance) ||
3583  gnc_commodity_equiv (balance_currency, new_currency))
3584  return balance;
3585 
3586  book = gnc_account_get_book (acc);
3587  pdb = gnc_pricedb_get_db (book);
3588 
3590  pdb, balance, balance_currency, new_currency, date);
3591 
3592  return balance;
3593 }
3594 
3595 /*
3596  * Given an account and a GetBalanceFn pointer, extract the requested
3597  * balance from the account and then convert it to the desired
3598  * currency.
3599  */
3600 static gnc_numeric
3601 xaccAccountGetXxxBalanceInCurrency (const Account *acc,
3602  xaccGetBalanceFn fn,
3603  const gnc_commodity *report_currency)
3604 {
3605  AccountPrivate *priv;
3606  gnc_numeric balance;
3607 
3608  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
3609  g_return_val_if_fail(fn, gnc_numeric_zero());
3610  g_return_val_if_fail(GNC_IS_COMMODITY(report_currency), gnc_numeric_zero());
3611 
3612  priv = GET_PRIVATE(acc);
3613  balance = fn(acc);
3614  balance = xaccAccountConvertBalanceToCurrency(acc, balance,
3615  priv->commodity,
3616  report_currency);
3617  return balance;
3618 }
3619 
3620 static gnc_numeric
3621 xaccAccountGetXxxBalanceAsOfDateInCurrency(Account *acc, time64 date,
3622  xaccGetBalanceAsOfDateFn fn,
3623  const gnc_commodity *report_commodity)
3624 {
3625  AccountPrivate *priv;
3626 
3627  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
3628  g_return_val_if_fail(fn, gnc_numeric_zero());
3629  g_return_val_if_fail(GNC_IS_COMMODITY(report_commodity), gnc_numeric_zero());
3630 
3631  priv = GET_PRIVATE(acc);
3632  return xaccAccountConvertBalanceToCurrencyAsOfDate(
3633  acc, fn(acc, date), priv->commodity, report_commodity, date);
3634 }
3635 
3636 /*
3637  * Data structure used to pass various arguments into the following fn.
3638  */
3639 typedef struct
3640 {
3641  const gnc_commodity *currency;
3642  gnc_numeric balance;
3643  xaccGetBalanceFn fn;
3644  xaccGetBalanceAsOfDateFn asOfDateFn;
3645  time64 date;
3646 } CurrencyBalance;
3647 
3648 
3649 /*
3650  * A helper function for iterating over all the accounts in a list or
3651  * tree. This function is called once per account, and sums up the
3652  * values of all these accounts.
3653  */
3654 static void
3655 xaccAccountBalanceHelper (Account *acc, gpointer data)
3656 {
3657  CurrencyBalance *cb = static_cast<CurrencyBalance*>(data);
3658  gnc_numeric balance;
3659 
3660  if (!cb->fn || !cb->currency)
3661  return;
3662  balance = xaccAccountGetXxxBalanceInCurrency (acc, cb->fn, cb->currency);
3663  cb->balance = gnc_numeric_add (cb->balance, balance,
3664  gnc_commodity_get_fraction (cb->currency),
3666 }
3667 
3668 static void
3669 xaccAccountBalanceAsOfDateHelper (Account *acc, gpointer data)
3670 {
3671  CurrencyBalance *cb = static_cast<CurrencyBalance*>(data);
3672  gnc_numeric balance;
3673 
3674  g_return_if_fail (cb->asOfDateFn && cb->currency);
3675 
3676  balance = xaccAccountGetXxxBalanceAsOfDateInCurrency (
3677  acc, cb->date, cb->asOfDateFn, cb->currency);
3678  cb->balance = gnc_numeric_add (cb->balance, balance,
3679  gnc_commodity_get_fraction (cb->currency),
3681 }
3682 
3683 
3684 
3685 /*
3686  * Common function that iterates recursively over all accounts below
3687  * the specified account. It uses xaccAccountBalanceHelper to sum up
3688  * the balances of all its children, and uses the specified function
3689  * 'fn' for extracting the balance. This function may extract the
3690  * current value, the reconciled value, etc.
3691  *
3692  * If 'report_commodity' is nullptr, just use the account's commodity.
3693  * If 'include_children' is FALSE, this function doesn't recurse at all.
3694  */
3695 static gnc_numeric
3696 xaccAccountGetXxxBalanceInCurrencyRecursive (const Account *acc,
3697  xaccGetBalanceFn fn,
3698  const gnc_commodity *report_commodity,
3699  gboolean include_children)
3700 {
3701  gnc_numeric balance;
3702 
3703  if (!acc) return gnc_numeric_zero ();
3704  if (!report_commodity)
3705  report_commodity = xaccAccountGetCommodity (acc);
3706  if (!report_commodity)
3707  return gnc_numeric_zero();
3708 
3709  balance = xaccAccountGetXxxBalanceInCurrency (acc, fn, report_commodity);
3710 
3711  /* If needed, sum up the children converting to the *requested*
3712  commodity. */
3713  if (include_children)
3714  {
3715 #ifdef _MSC_VER
3716  /* MSVC compiler: Somehow, the struct initialization containing a
3717  gnc_numeric doesn't work. As an exception, we hand-initialize
3718  that member afterwards. */
3719  CurrencyBalance cb = { report_commodity, { 0 }, fn, nullptr, 0 };
3720  cb.balance = balance;
3721 #else
3722  CurrencyBalance cb = { report_commodity, balance, fn, nullptr, 0 };
3723 #endif
3724 
3725  gnc_account_foreach_descendant (acc, xaccAccountBalanceHelper, &cb);
3726  balance = cb.balance;
3727  }
3728 
3729  return balance;
3730 }
3731 
3732 static gnc_numeric
3733 xaccAccountGetXxxBalanceAsOfDateInCurrencyRecursive (
3734  Account *acc, time64 date, xaccGetBalanceAsOfDateFn fn,
3735  const gnc_commodity *report_commodity, gboolean include_children)
3736 {
3737  gnc_numeric balance;
3738 
3739  g_return_val_if_fail(acc, gnc_numeric_zero());
3740  if (!report_commodity)
3741  report_commodity = xaccAccountGetCommodity (acc);
3742  if (!report_commodity)
3743  return gnc_numeric_zero();
3744 
3745  balance = xaccAccountGetXxxBalanceAsOfDateInCurrency(
3746  acc, date, fn, report_commodity);
3747 
3748  /* If needed, sum up the children converting to the *requested*
3749  commodity. */
3750  if (include_children)
3751  {
3752 #ifdef _MSC_VER
3753  /* MSVC compiler: Somehow, the struct initialization containing a
3754  gnc_numeric doesn't work. As an exception, we hand-initialize
3755  that member afterwards. */
3756  CurrencyBalance cb = { report_commodity, 0, nullptr, fn, date };
3757  cb.balance = balance;
3758 #else
3759  CurrencyBalance cb = { report_commodity, balance, nullptr, fn, date };
3760 #endif
3761 
3762  gnc_account_foreach_descendant (acc, xaccAccountBalanceAsOfDateHelper, &cb);
3763  balance = cb.balance;
3764  }
3765 
3766  return balance;
3767 }
3768 
3769 gnc_numeric
3770 xaccAccountGetBalanceInCurrency (const Account *acc,
3771  const gnc_commodity *report_commodity,
3772  gboolean include_children)
3773 {
3774  gnc_numeric rc;
3775  rc = xaccAccountGetXxxBalanceInCurrencyRecursive (
3776  acc, xaccAccountGetBalance, report_commodity, include_children);
3777  PINFO(" baln=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, rc.num, rc.denom);
3778  return rc;
3779 }
3780 
3781 
3782 gnc_numeric
3783 xaccAccountGetClearedBalanceInCurrency (const Account *acc,
3784  const gnc_commodity *report_commodity,
3785  gboolean include_children)
3786 {
3787  return xaccAccountGetXxxBalanceInCurrencyRecursive (
3788  acc, xaccAccountGetClearedBalance, report_commodity,
3789  include_children);
3790 }
3791 
3792 gnc_numeric
3793 xaccAccountGetReconciledBalanceInCurrency (const Account *acc,
3794  const gnc_commodity *report_commodity,
3795  gboolean include_children)
3796 {
3797  return xaccAccountGetXxxBalanceInCurrencyRecursive (
3798  acc, xaccAccountGetReconciledBalance, report_commodity,
3799  include_children);
3800 }
3801 
3802 gnc_numeric
3803 xaccAccountGetPresentBalanceInCurrency (const Account *acc,
3804  const gnc_commodity *report_commodity,
3805  gboolean include_children)
3806 {
3807  return xaccAccountGetXxxBalanceAsOfDateInCurrencyRecursive (
3809  report_commodity,
3810  include_children);
3811 }
3812 
3813 gnc_numeric
3814 xaccAccountGetProjectedMinimumBalanceInCurrency (
3815  const Account *acc,
3816  const gnc_commodity *report_commodity,
3817  gboolean include_children)
3818 {
3819  return xaccAccountGetXxxBalanceInCurrencyRecursive (
3820  acc, xaccAccountGetProjectedMinimumBalance, report_commodity,
3821  include_children);
3822 }
3823 
3824 gnc_numeric
3826  Account *acc, time64 date, gnc_commodity *report_commodity,
3827  gboolean include_children)
3828 {
3829  return xaccAccountGetXxxBalanceAsOfDateInCurrencyRecursive (
3830  acc, date, xaccAccountGetBalanceAsOfDate, report_commodity,
3831  include_children);
3832 }
3833 
3834 gnc_numeric
3836  Account *acc, time64 date, gnc_commodity *report_commodity,
3837  gboolean include_children)
3838 {
3839  return xaccAccountGetXxxBalanceAsOfDateInCurrencyRecursive
3840  (acc, date, xaccAccountGetNoclosingBalanceAsOfDate,
3841  report_commodity, include_children);
3842 }
3843 
3844 gnc_numeric
3845 xaccAccountGetBalanceChangeForPeriod (Account *acc, time64 t1, time64 t2,
3846  gboolean recurse)
3847 {
3848  gnc_numeric b1, b2;
3849 
3850  b1 = xaccAccountGetBalanceAsOfDateInCurrency(acc, t1, nullptr, recurse);
3851  b2 = xaccAccountGetBalanceAsOfDateInCurrency(acc, t2, nullptr, recurse);
3853 }
3854 
3855 gnc_numeric
3856 xaccAccountGetNoclosingBalanceChangeForPeriod (Account *acc, time64 t1,
3857  time64 t2, gboolean recurse)
3858 {
3859  gnc_numeric b1, b2;
3860 
3861  b1 = xaccAccountGetNoclosingBalanceAsOfDateInCurrency(acc, t1, nullptr, recurse);
3862  b2 = xaccAccountGetNoclosingBalanceAsOfDateInCurrency(acc, t2, nullptr, recurse);
3864 }
3865 
3866 typedef struct
3867 {
3868  const gnc_commodity *currency;
3869  gnc_numeric balanceChange;
3870  time64 t1;
3871  time64 t2;
3873 
3874 static void
3875 xaccAccountBalanceChangeHelper (Account *acc, gpointer data)
3876 {
3877  CurrencyBalanceChange *cbdiff = static_cast<CurrencyBalanceChange*>(data);
3878 
3879  gnc_numeric b1, b2;
3880  b1 = GetBalanceAsOfDate(acc, cbdiff->t1, xaccSplitGetNoclosingBalance);
3881  b2 = GetBalanceAsOfDate(acc, cbdiff->t2, xaccSplitGetNoclosingBalance);
3882  gnc_numeric balanceChange = gnc_numeric_sub(b2, b1, GNC_DENOM_AUTO, GNC_HOW_DENOM_FIXED);
3883  gnc_numeric balanceChange_conv = xaccAccountConvertBalanceToCurrencyAsOfDate(acc, balanceChange, xaccAccountGetCommodity(acc), cbdiff->currency, cbdiff->t2);
3884  cbdiff->balanceChange = gnc_numeric_add (cbdiff->balanceChange, balanceChange_conv,
3885  gnc_commodity_get_fraction (cbdiff->currency),
3887 }
3888 
3889 gnc_numeric
3890 xaccAccountGetNoclosingBalanceChangeInCurrencyForPeriod (Account *acc, time64 t1,
3891  time64 t2, gboolean recurse)
3892 {
3893 
3894 
3895  gnc_numeric b1, b2;
3896  b1 = GetBalanceAsOfDate(acc, t1, xaccSplitGetNoclosingBalance);
3897  b2 = GetBalanceAsOfDate(acc, t2, xaccSplitGetNoclosingBalance);
3898  gnc_numeric balanceChange = gnc_numeric_sub(b2, b1, GNC_DENOM_AUTO, GNC_HOW_DENOM_FIXED);
3899 
3900  gnc_commodity *report_commodity = xaccAccountGetCommodity(acc);
3901  CurrencyBalanceChange cbdiff = { report_commodity, balanceChange, t1, t2 };
3902 
3903  if(recurse)
3904  {
3905  gnc_account_foreach_descendant (acc, xaccAccountBalanceChangeHelper, &cbdiff);
3906  balanceChange = cbdiff.balanceChange;
3907  }
3908  return balanceChange;
3909 }
3910 
3911 /********************************************************************\
3912 \********************************************************************/
3913 
3914 const SplitsVec&
3915 xaccAccountGetSplits (const Account *account)
3916 {
3917  static const SplitsVec empty;
3918  g_return_val_if_fail (GNC_IS_ACCOUNT(account), empty);
3919  return GET_PRIVATE(account)->splits;
3920 }
3921 
3922 SplitList *
3924 {
3925  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
3926  auto priv{GET_PRIVATE(acc)};
3927  return std::accumulate (priv->splits.rbegin(), priv->splits.rend(),
3928  static_cast<GList*>(nullptr), g_list_prepend);
3929 }
3930 
3931 size_t
3932 xaccAccountGetSplitsSize (const Account *account)
3933 {
3934  g_return_val_if_fail (GNC_IS_ACCOUNT(account), 0);
3935  return GNC_IS_ACCOUNT(account) ? GET_PRIVATE(account)->splits.size() : 0;
3936 }
3937 
3938 gboolean gnc_account_and_descendants_empty (Account *acc)
3939 {
3940  g_return_val_if_fail (GNC_IS_ACCOUNT (acc), FALSE);
3941  auto priv = GET_PRIVATE (acc);
3942  if (!priv->splits.empty()) return FALSE;
3943  return std::all_of (priv->children.begin(), priv->children.end(),
3944  gnc_account_and_descendants_empty);
3945 }
3946 
3947 LotList *
3949 {
3950  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
3951  return g_list_copy(GET_PRIVATE(acc)->lots);
3952 }
3953 
3954 LotList *
3956  gboolean (*match_func)(GNCLot *lot,
3957  gpointer user_data),
3958  gpointer user_data, GCompareFunc sort_func)
3959 {
3960  AccountPrivate *priv;
3961  GList *lot_list;
3962  GList *retval = nullptr;
3963 
3964  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
3965 
3966  priv = GET_PRIVATE(acc);
3967  for (lot_list = priv->lots; lot_list; lot_list = lot_list->next)
3968  {
3969  GNCLot *lot = static_cast<GNCLot*>(lot_list->data);
3970 
3971  /* If this lot is closed, then ignore it */
3972  if (gnc_lot_is_closed (lot))
3973  continue;
3974 
3975  if (match_func && !(match_func)(lot, user_data))
3976  continue;
3977 
3978  /* Ok, this is a valid lot. Add it to our list of lots */
3979  retval = g_list_prepend (retval, lot);
3980  }
3981 
3982  if (sort_func)
3983  retval = g_list_sort (retval, sort_func);
3984 
3985  return retval;
3986 }
3987 
3988 gpointer
3989 xaccAccountForEachLot(const Account *acc,
3990  gpointer (*proc)(GNCLot *lot, void *data), void *data)
3991 {
3992  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
3993  g_return_val_if_fail(proc, nullptr);
3994 
3995  for (auto node = GET_PRIVATE(acc)->lots; node; node = node->next)
3996  if (auto result = proc(GNC_LOT(node->data), data))
3997  return result;
3998 
3999  return nullptr;
4000 }
4001 
4002 
4003 /********************************************************************\
4004 \********************************************************************/
4005 
4006 /* These functions use interchange gint64 and gboolean. Is that right? */
4007 gboolean
4009 {
4010  return get_kvp_boolean_path(acc, {"tax-related"});
4011 }
4012 
4013 void
4014 xaccAccountSetTaxRelated (Account *acc, gboolean tax_related)
4015 {
4016  set_kvp_boolean_path(acc, {"tax-related"}, tax_related);
4017 }
4018 
4019 const char *
4021 {
4022  return get_kvp_string_path (acc, {"tax-US", "code"});
4023 }
4024 
4025 void
4026 xaccAccountSetTaxUSCode (Account *acc, const char *code)
4027 {
4028  set_kvp_string_path (acc, {"tax-US", "code"}, code);
4029 }
4030 
4031 const char *
4033 {
4034  return get_kvp_string_path (acc, {"tax-US", "payer-name-source"});
4035 }
4036 
4037 void
4039 {
4040  set_kvp_string_path (acc, {"tax-US", "payer-name-source"}, source);
4041 }
4042 
4043 gint64
4045 {
4046  auto copy_number = get_kvp_int64_path (acc, {"tax-US", "copy-number"});
4047  return (copy_number && (*copy_number != 0)) ? *copy_number : 1;
4048 }
4049 
4050 void
4051 xaccAccountSetTaxUSCopyNumber (Account *acc, gint64 copy_number)
4052 {
4053  if (copy_number != 0)
4054  set_kvp_int64_path (acc, {"tax-US", "copy-number"}, copy_number);
4055  else
4056  /* deletes KVP if it exists */
4057  set_kvp_int64_path (acc, {"tax-US", "copy-number"}, std::nullopt);
4058 }
4059 
4060 /*********************************************************************\
4061 \ ********************************************************************/
4062 
4063 
4065 {
4066  if (gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_ACCOUNTING_LABELS))
4067  return _(dflt_acct_debit_str);
4068 
4069  auto result = gnc_acct_debit_strs.find(acct_type);
4070  if (result != gnc_acct_debit_strs.end())
4071  return _(result->second);
4072  else
4073  return _(dflt_acct_debit_str);
4074 }
4075 
4077 {
4078  if (gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_ACCOUNTING_LABELS))
4079  return _(dflt_acct_credit_str);
4080 
4081  auto result = gnc_acct_credit_strs.find(acct_type);
4082  if (result != gnc_acct_credit_strs.end())
4083  return _(result->second);
4084  else
4085  return _(dflt_acct_credit_str);
4086 }
4087 
4088 /********************************************************************\
4089 \********************************************************************/
4090 
4091 gboolean
4093 {
4094  return get_kvp_boolean_path(acc, {"placeholder"});
4095 }
4096 
4097 void
4099 {
4100  set_kvp_boolean_path(acc, {"placeholder"}, val);
4101 }
4102 
4103 gboolean
4105 {
4106  return get_kvp_boolean_path(acc, {"import-append-text"});
4107 }
4108 
4109 void
4110 xaccAccountSetAppendText (Account *acc, gboolean val)
4111 {
4112  set_kvp_boolean_path(acc, {"import-append-text"}, val);
4113 }
4114 
4115 gboolean
4117 {
4118  g_return_val_if_fail (GNC_IS_ACCOUNT(acc), false);
4119  if (GET_PRIVATE(acc)->type != ACCT_TYPE_EQUITY)
4120  return false;
4121 
4122  return !g_strcmp0 (get_kvp_string_path (acc, {"equity-type"}), "opening-balance");
4123 }
4124 
4125 void
4127 {
4128  g_return_if_fail (GNC_IS_ACCOUNT(acc));
4129  if (GET_PRIVATE(acc)->type != ACCT_TYPE_EQUITY)
4130  return;
4131  set_kvp_string_path(acc, {"equity-type"}, val ? "opening-balance" : nullptr);
4132 }
4133 
4136 {
4137  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), PLACEHOLDER_NONE);
4138  if (xaccAccountGetPlaceholder(acc)) return PLACEHOLDER_THIS;
4139 
4140  return gnc_account_foreach_descendant_until (acc, (AccountCb2)xaccAccountGetPlaceholder, nullptr)
4141  ? PLACEHOLDER_CHILD : PLACEHOLDER_NONE;
4142 }
4143 
4144 /********************************************************************\
4145  \********************************************************************/
4146 
4147 gboolean
4149 {
4150  return get_kvp_boolean_path (acc, {KEY_RECONCILE_INFO, "auto-interest-transfer"});
4151 }
4152 
4153 void
4155 {
4156  set_kvp_boolean_path (acc, {KEY_RECONCILE_INFO, "auto-interest-transfer"}, val);
4157 }
4158 
4159 /********************************************************************\
4160 \********************************************************************/
4161 
4162 gboolean
4164 {
4165  return get_kvp_boolean_path (acc, {"hidden"});
4166 }
4167 
4168 void
4169 xaccAccountSetHidden (Account *acc, gboolean val)
4170 {
4171  set_kvp_boolean_path (acc, {"hidden"}, val);
4172 }
4173 
4174 gboolean
4176 {
4177  AccountPrivate *priv;
4178 
4179  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
4180 
4181  if (xaccAccountGetHidden(acc))
4182  return TRUE;
4183  priv = GET_PRIVATE(acc);
4184  while ((acc = priv->parent) != nullptr)
4185  {
4186  priv = GET_PRIVATE(acc);
4187  if (xaccAccountGetHidden(acc))
4188  return TRUE;
4189  }
4190  return FALSE;
4191 }
4192 
4193 /********************************************************************\
4194 \********************************************************************/
4195 
4196 gboolean
4197 xaccAccountHasAncestor (const Account *acc, const Account * ancestor)
4198 {
4199  const Account *parent;
4200 
4201  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
4202  g_return_val_if_fail(GNC_IS_ACCOUNT(ancestor), FALSE);
4203 
4204  parent = acc;
4205  while (parent && parent != ancestor)
4206  parent = GET_PRIVATE(parent)->parent;
4207 
4208  return (parent == ancestor);
4209 }
4210 
4211 /********************************************************************\
4212 \********************************************************************/
4213 
4214 /* You must edit the functions in this block in tandem. KEEP THEM IN
4215  SYNC! */
4216 
4217 #define GNC_RETURN_ENUM_AS_STRING(x) case (ACCT_TYPE_ ## x): return #x;
4218 
4219 const char *
4221 {
4222  switch (type)
4223  {
4224  GNC_RETURN_ENUM_AS_STRING(NONE);
4225  GNC_RETURN_ENUM_AS_STRING(BANK);
4226  GNC_RETURN_ENUM_AS_STRING(CASH);
4227  GNC_RETURN_ENUM_AS_STRING(CREDIT);
4228  GNC_RETURN_ENUM_AS_STRING(ASSET);
4229  GNC_RETURN_ENUM_AS_STRING(LIABILITY);
4230  GNC_RETURN_ENUM_AS_STRING(STOCK);
4231  GNC_RETURN_ENUM_AS_STRING(MUTUAL);
4232  GNC_RETURN_ENUM_AS_STRING(CURRENCY);
4233  GNC_RETURN_ENUM_AS_STRING(INCOME);
4234  GNC_RETURN_ENUM_AS_STRING(EXPENSE);
4235  GNC_RETURN_ENUM_AS_STRING(EQUITY);
4236  GNC_RETURN_ENUM_AS_STRING(RECEIVABLE);
4237  GNC_RETURN_ENUM_AS_STRING(PAYABLE);
4238  GNC_RETURN_ENUM_AS_STRING(ROOT);
4239  GNC_RETURN_ENUM_AS_STRING(TRADING);
4240  GNC_RETURN_ENUM_AS_STRING(CHECKING);
4241  GNC_RETURN_ENUM_AS_STRING(SAVINGS);
4242  GNC_RETURN_ENUM_AS_STRING(MONEYMRKT);
4243  GNC_RETURN_ENUM_AS_STRING(CREDITLINE);
4244  default:
4245  PERR ("asked to translate unknown account type %d.\n", type);
4246  break;
4247  }
4248  return(nullptr);
4249 }
4250 
4251 #undef GNC_RETURN_ENUM_AS_STRING
4252 
4253 #define GNC_RETURN_ON_MATCH(x) \
4254  if(g_strcmp0(#x, (str)) == 0) { *type = ACCT_TYPE_ ## x; return(TRUE); }
4255 
4256 gboolean
4258 {
4259 
4260  GNC_RETURN_ON_MATCH(NONE);
4261  GNC_RETURN_ON_MATCH(BANK);
4262  GNC_RETURN_ON_MATCH(CASH);
4263  GNC_RETURN_ON_MATCH(CREDIT);
4264  GNC_RETURN_ON_MATCH(ASSET);
4265  GNC_RETURN_ON_MATCH(LIABILITY);
4266  GNC_RETURN_ON_MATCH(STOCK);
4267  GNC_RETURN_ON_MATCH(MUTUAL);
4268  GNC_RETURN_ON_MATCH(CURRENCY);
4269  GNC_RETURN_ON_MATCH(INCOME);
4270  GNC_RETURN_ON_MATCH(EXPENSE);
4271  GNC_RETURN_ON_MATCH(EQUITY);
4272  GNC_RETURN_ON_MATCH(RECEIVABLE);
4273  GNC_RETURN_ON_MATCH(PAYABLE);
4274  GNC_RETURN_ON_MATCH(ROOT);
4275  GNC_RETURN_ON_MATCH(TRADING);
4276  GNC_RETURN_ON_MATCH(CHECKING);
4277  GNC_RETURN_ON_MATCH(SAVINGS);
4278  GNC_RETURN_ON_MATCH(MONEYMRKT);
4279  GNC_RETURN_ON_MATCH(CREDITLINE);
4280 
4281  PERR("asked to translate unknown account type string %s.\n",
4282  str ? str : "(null)");
4283 
4284  return(FALSE);
4285 }
4286 
4287 #undef GNC_RETURN_ON_MATCH
4288 
4289 /* impedance mismatch is a source of loss */
4291 xaccAccountStringToEnum(const char* str)
4292 {
4293  GNCAccountType type;
4294  gboolean rc;
4295  rc = xaccAccountStringToType(str, &type);
4296  if (FALSE == rc) return ACCT_TYPE_INVALID;
4297  return type;
4298 }
4299 
4300 /********************************************************************\
4301 \********************************************************************/
4302 
4303 static char const *
4304 account_type_name[NUM_ACCOUNT_TYPES] =
4305 {
4306  N_("Bank"),
4307  N_("Cash"),
4308  N_("Asset"),
4309  N_("Credit Card"),
4310  N_("Liability"),
4311  N_("Stock"),
4312  N_("Mutual Fund"),
4313  N_("Currency"),
4314  N_("Income"),
4315  N_("Expense"),
4316  N_("Equity"),
4317  N_("A/Receivable"),
4318  N_("A/Payable"),
4319  N_("Root"),
4320  N_("Trading")
4321  /*
4322  N_("Checking"),
4323  N_("Savings"),
4324  N_("Money Market"),
4325  N_("Credit Line")
4326  */
4327 };
4328 
4329 const char *
4331 {
4332  if (type < 0 || NUM_ACCOUNT_TYPES <= type ) return "";
4333  return _(account_type_name [type]);
4334 }
4335 
4336 /********************************************************************\
4337 \********************************************************************/
4338 
4339 guint32
4341 {
4342  switch (type)
4343  {
4344  case ACCT_TYPE_BANK:
4345  case ACCT_TYPE_CASH:
4346  case ACCT_TYPE_ASSET:
4347  case ACCT_TYPE_CREDIT:
4348  case ACCT_TYPE_LIABILITY:
4349  case ACCT_TYPE_INCOME:
4350  case ACCT_TYPE_EXPENSE:
4351  case ACCT_TYPE_EQUITY:
4352  return
4353  (1 << ACCT_TYPE_BANK) |
4354  (1 << ACCT_TYPE_CASH) |
4355  (1 << ACCT_TYPE_ASSET) |
4356  (1 << ACCT_TYPE_CREDIT) |
4357  (1 << ACCT_TYPE_LIABILITY) |
4358  (1 << ACCT_TYPE_INCOME) |
4359  (1 << ACCT_TYPE_EXPENSE) |
4360  (1 << ACCT_TYPE_EQUITY);
4361  case ACCT_TYPE_STOCK:
4362  case ACCT_TYPE_MUTUAL:
4363  case ACCT_TYPE_CURRENCY:
4364  return
4365  (1 << ACCT_TYPE_STOCK) |
4366  (1 << ACCT_TYPE_MUTUAL) |
4367  (1 << ACCT_TYPE_CURRENCY);
4368  case ACCT_TYPE_RECEIVABLE:
4369  return (1 << ACCT_TYPE_RECEIVABLE);
4370  case ACCT_TYPE_PAYABLE:
4371  return (1 << ACCT_TYPE_PAYABLE);
4372  case ACCT_TYPE_TRADING:
4373  return (1 << ACCT_TYPE_TRADING);
4374  default:
4375  PERR("bad account type: %d", type);
4376  return 0;
4377  }
4378 }
4379 guint32
4381 {
4382  switch (type)
4383  {
4384  case ACCT_TYPE_BANK:
4385  case ACCT_TYPE_CASH:
4386  case ACCT_TYPE_ASSET:
4387  case ACCT_TYPE_STOCK:
4388  case ACCT_TYPE_MUTUAL:
4389  case ACCT_TYPE_CURRENCY:
4390  case ACCT_TYPE_CREDIT:
4391  case ACCT_TYPE_LIABILITY:
4392  case ACCT_TYPE_RECEIVABLE:
4393  case ACCT_TYPE_PAYABLE:
4394  return
4395  (1 << ACCT_TYPE_BANK) |
4396  (1 << ACCT_TYPE_CASH) |
4397  (1 << ACCT_TYPE_ASSET) |
4398  (1 << ACCT_TYPE_STOCK) |
4399  (1 << ACCT_TYPE_MUTUAL) |
4400  (1 << ACCT_TYPE_CURRENCY) |
4401  (1 << ACCT_TYPE_CREDIT) |
4402  (1 << ACCT_TYPE_LIABILITY) |
4403  (1 << ACCT_TYPE_RECEIVABLE) |
4404  (1 << ACCT_TYPE_PAYABLE) |
4405  (1 << ACCT_TYPE_ROOT);
4406  case ACCT_TYPE_INCOME:
4407  case ACCT_TYPE_EXPENSE:
4408  return
4409  (1 << ACCT_TYPE_INCOME) |
4410  (1 << ACCT_TYPE_EXPENSE) |
4411  (1 << ACCT_TYPE_ROOT);
4412  case ACCT_TYPE_EQUITY:
4413  return
4414  (1 << ACCT_TYPE_EQUITY) |
4415  (1 << ACCT_TYPE_ROOT);
4416  case ACCT_TYPE_TRADING:
4417  return
4418  (1 << ACCT_TYPE_TRADING) |
4419  (1 << ACCT_TYPE_ROOT);
4420  default:
4421  PERR("bad account type: %d", type);
4422  return 0;
4423  }
4424 }
4425 
4426 gboolean
4428  GNCAccountType child_type)
4429 {
4430  /* ACCT_TYPE_NONE isn't compatible with anything, even ACCT_TYPE_NONE. */
4431  if (parent_type == ACCT_TYPE_NONE || child_type == ACCT_TYPE_NONE)
4432  return FALSE;
4433 
4434  /* ACCT_TYPE_ROOT can't have a parent account, and asking will raise
4435  * an error. */
4436  if (child_type == ACCT_TYPE_ROOT)
4437  return FALSE;
4438 
4439  return ((xaccParentAccountTypesCompatibleWith (child_type) &
4440  (1 << parent_type))
4441  != 0);
4442 }
4443 
4444 guint32
4446 {
4447  guint32 mask = (1 << NUM_ACCOUNT_TYPES) - 1;
4448  mask &= ~((1 << ACCT_TYPE_CURRENCY) | /* DEPRECATED */
4449  (1 << ACCT_TYPE_ROOT)); /* ROOT */
4450 
4451  return mask;
4452 }
4453 
4455 {
4456  switch (t)
4457  {
4458  case ACCT_TYPE_RECEIVABLE:
4459  case ACCT_TYPE_PAYABLE:
4460  return FALSE;
4461  default:
4464  }
4465 }
4466 
4469 {
4470  switch (t)
4471  {
4472  case ACCT_TYPE_BANK:
4473  case ACCT_TYPE_STOCK:
4474  case ACCT_TYPE_MONEYMRKT:
4475  case ACCT_TYPE_CHECKING:
4476  case ACCT_TYPE_SAVINGS:
4477  case ACCT_TYPE_MUTUAL:
4478  case ACCT_TYPE_CURRENCY:
4479  case ACCT_TYPE_CASH:
4480  case ACCT_TYPE_ASSET:
4481  case ACCT_TYPE_RECEIVABLE:
4482  return ACCT_TYPE_ASSET;
4483  case ACCT_TYPE_CREDIT:
4484  case ACCT_TYPE_LIABILITY:
4485  case ACCT_TYPE_PAYABLE:
4486  case ACCT_TYPE_CREDITLINE:
4487  return ACCT_TYPE_LIABILITY;
4488  case ACCT_TYPE_INCOME:
4489  return ACCT_TYPE_INCOME;
4490  case ACCT_TYPE_EXPENSE:
4491  return ACCT_TYPE_EXPENSE;
4492  case ACCT_TYPE_EQUITY:
4493  return ACCT_TYPE_EQUITY;
4494  case ACCT_TYPE_TRADING:
4495  default:
4496  return ACCT_TYPE_NONE;
4497  }
4498 }
4499 
4501 {
4502  switch (t)
4503  {
4504  case ACCT_TYPE_RECEIVABLE:
4505  case ACCT_TYPE_PAYABLE:
4506  return TRUE;
4507  default:
4508  return FALSE;
4509  }
4510 }
4511 
4513 {
4514  switch (t)
4515  {
4516  case ACCT_TYPE_EQUITY:
4517  return TRUE;
4518  default:
4519  return FALSE;
4520  }
4521 }
4522 
4523 gboolean
4525 {
4526  AccountPrivate *priv;
4527 
4528  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
4529 
4530  priv = GET_PRIVATE(acc);
4531  return (priv->type == ACCT_TYPE_STOCK || priv->type == ACCT_TYPE_MUTUAL ||
4532  priv->type == ACCT_TYPE_CURRENCY);
4533 }
4534 
4535 /********************************************************************\
4536 \********************************************************************/
4537 
4538 gboolean
4540 {
4541  gboolean retval = FALSE;
4542  auto date = get_kvp_int64_path (acc, {KEY_RECONCILE_INFO, "last-date"});
4543 
4544  if (date)
4545  {
4546  if (last_date)
4547  *last_date = *date;
4548  retval = TRUE;
4549  }
4550  return retval;
4551 }
4552 
4553 /********************************************************************\
4554 \********************************************************************/
4555 
4556 void
4558 {
4559  set_kvp_int64_path (acc, {KEY_RECONCILE_INFO, "last-date"}, last_date);
4560 }
4561 
4562 /********************************************************************\
4563 \********************************************************************/
4564 
4565 gboolean
4567  int *months, int *days)
4568 {
4569  if (!acc) return FALSE;
4570  auto m{get_kvp_int64_path (acc, {KEY_RECONCILE_INFO, "last-interval", "months"})};
4571  auto d{get_kvp_int64_path (acc, {KEY_RECONCILE_INFO, "last-interval", "days"})};
4572  if (m && d)
4573  {
4574  if (months)
4575  *months = *m;
4576  if (days)
4577  *days = *d;
4578  return true;
4579  }
4580  return false;
4581 }
4582 
4583 /********************************************************************\
4584 \********************************************************************/
4585 
4586 void
4587 xaccAccountSetReconcileLastInterval (Account *acc, int months, int days)
4588 {
4589  set_kvp_int64_path (acc, {KEY_RECONCILE_INFO, "last-interval", "months"}, months);
4590  set_kvp_int64_path (acc, {KEY_RECONCILE_INFO, "last-interval", "days"}, days);
4591 }
4592 
4593 /********************************************************************\
4594 \********************************************************************/
4595 
4596 gboolean
4598 {
4599  if (auto date = get_kvp_int64_path (acc, {KEY_RECONCILE_INFO, KEY_POSTPONE, "date"}))
4600  {
4601  if (postpone_date)
4602  *postpone_date = *date;
4603  return true;
4604  }
4605  return false;
4606 }
4607 
4608 /********************************************************************\
4609 \********************************************************************/
4610 
4611 void
4613 {
4614  set_kvp_int64_path (acc, {KEY_RECONCILE_INFO, KEY_POSTPONE, "date"}, postpone_date);
4615 }
4616 
4617 /********************************************************************\
4618 \********************************************************************/
4619 
4620 gboolean
4622  gnc_numeric *balance)
4623 {
4624  if (auto bal = get_kvp_gnc_numeric_path (acc, {KEY_RECONCILE_INFO, KEY_POSTPONE, "balance"}))
4625  {
4626  if (balance)
4627  *balance = *bal;
4628  return true;
4629  }
4630  return false;
4631 }
4632 
4633 /********************************************************************\
4634 \********************************************************************/
4635 
4636 void
4638 {
4639  set_kvp_gnc_numeric_path (acc, {KEY_RECONCILE_INFO, KEY_POSTPONE, "balance"}, balance);
4640 }
4641 
4642 /********************************************************************\
4643 
4644 \********************************************************************/
4645 
4646 void
4648 {
4649  set_kvp_gnc_numeric_path (acc, {KEY_RECONCILE_INFO, KEY_POSTPONE}, {});
4650 }
4651 
4652 /********************************************************************\
4653 \********************************************************************/
4654 
4655 const char *
4657 {
4658  return get_kvp_string_path (acc, {"last-num"});
4659 }
4660 
4661 /********************************************************************\
4662 \********************************************************************/
4663 
4664 void
4665 xaccAccountSetLastNum (Account *acc, const char *num)
4666 {
4667  set_kvp_string_path (acc, {"last-num"}, num);
4668 }
4669 
4670 
4671 /********************************************************************\
4672 \********************************************************************/
4673 
4674 static bool
4675 get_balance_limit (const Account* acc, const std::string& key, gnc_numeric* balance)
4676 {
4677  auto limit = get_kvp_gnc_numeric_path (acc, {KEY_BALANCE_LIMIT, key});
4678  if (limit)
4679  *balance = gnc_numeric_create (limit->num, limit->denom);
4680  return limit.has_value();
4681 }
4682 
4683 static void
4684 set_balance_limit (Account *acc, const std::string& key, std::optional<gnc_numeric> balance)
4685 {
4686  if (balance && gnc_numeric_check (*balance))
4687  return;
4688  set_kvp_gnc_numeric_path (acc, {KEY_BALANCE_LIMIT, key}, balance);
4689 }
4690 
4691 gboolean
4693  gnc_numeric *balance)
4694 {
4695  return get_balance_limit (acc, KEY_BALANCE_HIGHER_LIMIT_VALUE, balance);
4696 }
4697 
4698 gboolean
4700  gnc_numeric *balance)
4701 {
4702  return get_balance_limit (acc, KEY_BALANCE_LOWER_LIMIT_VALUE, balance);
4703 }
4704 
4705 void
4706 xaccAccountSetHigherBalanceLimit (Account *acc, gnc_numeric balance)
4707 {
4708  set_balance_limit (acc, KEY_BALANCE_HIGHER_LIMIT_VALUE, balance);
4709 }
4710 
4711 void
4712 xaccAccountSetLowerBalanceLimit (Account *acc, gnc_numeric balance)
4713 {
4714  set_balance_limit (acc, KEY_BALANCE_LOWER_LIMIT_VALUE, balance);
4715 }
4716 
4717 void
4719 {
4720  set_balance_limit (acc, KEY_BALANCE_HIGHER_LIMIT_VALUE, {});
4721 }
4722 
4723 void
4725 {
4726  set_balance_limit (acc, KEY_BALANCE_LOWER_LIMIT_VALUE, {});
4727 }
4728 
4729 gboolean
4731 {
4732  return get_kvp_boolean_path (acc, {KEY_BALANCE_LIMIT, KEY_BALANCE_INCLUDE_SUB_ACCTS});
4733 }
4734 
4735 void
4737 {
4738  set_kvp_boolean_path (acc, {KEY_BALANCE_LIMIT, KEY_BALANCE_INCLUDE_SUB_ACCTS}, inc_sub);
4739 }
4740 
4741 /********************************************************************\
4742 \********************************************************************/
4743 
4744 static Account *
4745 GetOrMakeOrphanAccount (Account *root, gnc_commodity * currency)
4746 {
4747  char * accname;
4748  Account * acc;
4749 
4750  g_return_val_if_fail (root, nullptr);
4751 
4752  /* build the account name */
4753  if (!currency)
4754  {
4755  PERR ("No currency specified!");
4756  return nullptr;
4757  }
4758 
4759  accname = g_strconcat (_("Orphaned Gains"), "-",
4760  gnc_commodity_get_mnemonic (currency), nullptr);
4761 
4762  /* See if we've got one of these going already ... */
4763  acc = gnc_account_lookup_by_name(root, accname);
4764 
4765  if (acc == nullptr)
4766  {
4767  /* Guess not. We'll have to build one. */
4768  acc = xaccMallocAccount (gnc_account_get_book(root));
4769  xaccAccountBeginEdit (acc);
4770  xaccAccountSetName (acc, accname);
4771  xaccAccountSetCommodity (acc, currency);
4773  xaccAccountSetDescription (acc, _("Realized Gain/Loss"));
4774  xaccAccountSetNotes (acc,
4775  _("Realized Gains or Losses from "
4776  "Commodity or Trading Accounts "
4777  "that haven't been recorded elsewhere."));
4778 
4779  /* Hang the account off the root. */
4780  gnc_account_append_child (root, acc);
4781  xaccAccountCommitEdit (acc);
4782  }
4783 
4784  g_free (accname);
4785 
4786  return acc;
4787 }
4788 
4789 Account *
4790 xaccAccountGainsAccount (Account *acc, gnc_commodity *curr)
4791 {
4792  Path path {KEY_LOT_MGMT, "gains-acct", gnc_commodity_get_unique_name (curr)};
4793  auto gains_account = get_kvp_account_path (acc, path);
4794 
4795  if (gains_account == nullptr) /* No gains account for this currency */
4796  {
4797  gains_account = GetOrMakeOrphanAccount (gnc_account_get_root (acc), curr);
4798  set_kvp_account_path (acc, path, gains_account);
4799  }
4800 
4801  return gains_account;
4802 }
4803 
4804 /********************************************************************\
4805 \********************************************************************/
4806 
4807 void
4808 dxaccAccountSetPriceSrc(Account *acc, const char *src)
4809 {
4810  if (!acc) return;
4811 
4812  if (xaccAccountIsPriced(acc))
4813  set_kvp_string_path (acc, {"old-price-source"}, src);
4814 }
4815 
4816 /********************************************************************\
4817 \********************************************************************/
4818 
4819 const char*
4821 {
4822  if (!acc) return nullptr;
4823 
4824  if (!xaccAccountIsPriced(acc)) return nullptr;
4825 
4826  return get_kvp_string_path (acc, {"old-price-source"});
4827 }
4828 
4829 /********************************************************************\
4830 \********************************************************************/
4831 
4832 void
4833 dxaccAccountSetQuoteTZ(Account *acc, const char *tz)
4834 {
4835  if (!acc) return;
4836  if (!xaccAccountIsPriced(acc)) return;
4837  set_kvp_string_path (acc, {"old-quote-tz"}, tz);
4838 }
4839 
4840 /********************************************************************\
4841 \********************************************************************/
4842 
4843 const char*
4845 {
4846  if (!acc) return nullptr;
4847  if (!xaccAccountIsPriced(acc)) return nullptr;
4848  return get_kvp_string_path (acc, {"old-quote-tz"});
4849 }
4850 
4851 /********************************************************************\
4852 \********************************************************************/
4853 
4854 void
4856 {
4857  /* Would have been nice to use G_TYPE_BOOLEAN, but the other
4858  * boolean kvps save the value as "true" or "false" and that would
4859  * be file-incompatible with this.
4860  */
4861  set_kvp_int64_path (acc, {KEY_RECONCILE_INFO, KEY_INCLUDE_CHILDREN}, status);
4862 }
4863 
4864 /********************************************************************\
4865 \********************************************************************/
4866 
4867 gboolean
4869 {
4870  /* access the account's kvp-data for status and return that, if no value
4871  * is found then we can assume not to include the children, that being
4872  * the default behaviour
4873  */
4874  return get_kvp_boolean_path (acc, {KEY_RECONCILE_INFO, KEY_INCLUDE_CHILDREN});
4875 }
4876 
4877 /********************************************************************\
4878 \********************************************************************/
4879 
4880 Split *
4881 xaccAccountFindSplitByDesc(const Account *acc, const char *description)
4882 {
4883  auto has_description = [description](const Split* s) -> bool
4884  { return !g_strcmp0 (description, xaccTransGetDescription (xaccSplitGetParent (s))); };
4885  return gnc_account_find_split (acc, has_description, true);
4886 }
4887 
4888 /* This routine is for finding a matching transaction in an account by
4889  * matching on the description field. [CAS: The rest of this comment
4890  * seems to belong somewhere else.] This routine is used for
4891  * auto-filling in registers with a default leading account. The
4892  * dest_trans is a transaction used for currency checking. */
4893 Transaction *
4894 xaccAccountFindTransByDesc(const Account *acc, const char *description)
4895 {
4896  auto split = xaccAccountFindSplitByDesc (acc, description);
4897  return split ? xaccSplitGetParent (split) : nullptr;
4898 }
4899 
4900 /* ================================================================ */
4901 /* Concatenation, Merging functions */
4902 
4903 void
4904 gnc_account_join_children (Account *to_parent, Account *from_parent)
4905 {
4906 
4907  /* errors */
4908  g_return_if_fail(GNC_IS_ACCOUNT(to_parent));
4909  g_return_if_fail(GNC_IS_ACCOUNT(from_parent));
4910 
4911  /* optimizations */
4912  auto from_priv = GET_PRIVATE(from_parent);
4913  if (from_priv->children.empty())
4914  return;
4915 
4916  ENTER (" ");
4917  auto children = from_priv->children;
4918  for (auto child : children)
4919  gnc_account_append_child(to_parent, child);
4920  LEAVE (" ");
4921 }
4922 /********************************************************************\
4923 \********************************************************************/
4924 
4925 void
4927 {
4928  g_return_if_fail(GNC_IS_ACCOUNT(parent));
4929 
4930  auto ppriv = GET_PRIVATE(parent);
4931  for (auto it_a = ppriv->children.begin(); it_a != ppriv->children.end(); it_a++)
4932  {
4933  auto acc_a = *it_a;
4934  auto priv_a = GET_PRIVATE(acc_a);
4935  for (auto it_b = std::next(it_a); it_b != ppriv->children.end(); it_b++)
4936  {
4937  auto acc_b = *it_b;
4938  auto priv_b = GET_PRIVATE(acc_b);
4939  if (0 != null_strcmp(priv_a->accountName, priv_b->accountName))
4940  continue;
4941  if (0 != null_strcmp(priv_a->accountCode, priv_b->accountCode))
4942  continue;
4943  if (0 != null_strcmp(priv_a->description, priv_b->description))
4944  continue;
4945  if (0 != null_strcmp(xaccAccountGetColor(acc_a),
4946  xaccAccountGetColor(acc_b)))
4947  continue;
4948  if (!gnc_commodity_equiv(priv_a->commodity, priv_b->commodity))
4949  continue;
4950  if (0 != null_strcmp(xaccAccountGetNotes(acc_a),
4951  xaccAccountGetNotes(acc_b)))
4952  continue;
4953  if (priv_a->type != priv_b->type)
4954  continue;
4955 
4956  /* consolidate children */
4957  if (!priv_b->children.empty())
4958  {
4959  auto work = priv_b->children;
4960  for (auto w : work)
4961  gnc_account_append_child (acc_a, w);
4962 
4963  qof_event_gen (&acc_a->inst, QOF_EVENT_MODIFY, nullptr);
4964  qof_event_gen (&acc_b->inst, QOF_EVENT_MODIFY, nullptr);
4965  }
4966 
4967  /* recurse to do the children's children */
4969 
4970  /* consolidate transactions */
4971  while (!priv_b->splits.empty())
4972  xaccSplitSetAccount (priv_b->splits.front(), acc_a);
4973 
4974  /* move back one before removal. next iteration around the loop
4975  * will get the node after node_b */
4976  it_b--;
4977 
4978  /* The destroy function will remove from list -- node_a is ok,
4979  * it's before node_b */
4980  xaccAccountBeginEdit (acc_b);
4981  xaccAccountDestroy (acc_b);
4982  }
4983  }
4984 }
4985 
4986 /* ================================================================ */
4987 /* Transaction Traversal functions */
4988 
4989 
4990 static void
4991 xaccSplitsBeginStagedTransactionTraversals (SplitsVec& splits)
4992 {
4993  for (auto s : splits)
4994  {
4995  Transaction *trans = s->parent;
4996 
4997  if (trans)
4998  trans->marker = 0;
4999  }
5000 }
5001 
5002 /* original function */
5003 void
5005 {
5006  if (!account)
5007  return;
5008  xaccSplitsBeginStagedTransactionTraversals(GET_PRIVATE (account)->splits);
5009 }
5010 
5011 gboolean
5012 xaccTransactionTraverse (Transaction *trans, int stage)
5013 {
5014  if (trans == nullptr) return FALSE;
5015 
5016  if (trans->marker < stage)
5017  {
5018  trans->marker = stage;
5019  return TRUE;
5020  }
5021 
5022  return FALSE;
5023 }
5024 
5025 /* Replacement for xaccGroupBeginStagedTransactionTraversals */
5026 void
5028 {
5029  auto do_one_account = [](auto acc)
5030  { gnc_account_foreach_split (acc, [](auto s){ s->parent->marker = 0; }); };
5031  gnc_account_foreach_descendant (account, do_one_account);
5032 }
5033 
5034 int
5036  unsigned int stage,
5037  TransactionCallback thunk,
5038  void *cb_data)
5039 {
5040  if (!acc) return 0;
5041 
5042  // iterate on copy of splits. some callers modify the splitsvec.
5043  auto splits = GET_PRIVATE(acc)->splits;
5044  for (auto s : splits)
5045  {
5046  auto trans = s->parent;
5047  if (trans && (trans->marker < stage))
5048  {
5049  trans->marker = stage;
5050  if (thunk)
5051  {
5052  auto retval = thunk(trans, cb_data);
5053  if (retval) return retval;
5054  }
5055  }
5056  }
5057 
5058  return 0;
5059 }
5060 
5061 int
5063  unsigned int stage,
5064  TransactionCallback thunk,
5065  void *cb_data)
5066 {
5067  const AccountPrivate *priv;
5068  Transaction *trans;
5069  int retval;
5070 
5071  if (!acc) return 0;
5072 
5073  /* depth first traversal */
5074  priv = GET_PRIVATE(acc);
5075  for (auto acc_p : priv->children)
5076  {
5077  retval = gnc_account_tree_staged_transaction_traversal(acc_p, stage, thunk, cb_data);
5078  if (retval) return retval;
5079  }
5080 
5081  /* Now this account */
5082  for (auto s : priv->splits)
5083  {
5084  trans = s->parent;
5085  if (trans && (trans->marker < stage))
5086  {
5087  trans->marker = stage;
5088  if (thunk)
5089  {
5090  retval = thunk(trans, cb_data);
5091  if (retval) return retval;
5092  }
5093  }
5094  }
5095 
5096  return 0;
5097 }
5098 
5099 time64
5101 {
5102  g_return_val_if_fail (GNC_IS_ACCOUNT(account), INT64_MAX);
5103  const auto& splits = xaccAccountGetSplits (account);
5104  return splits.empty() ? INT64_MAX : xaccTransGetDate (xaccSplitGetParent (splits.front()));
5105 }
5106 
5107 /********************************************************************\
5108 \********************************************************************/
5109 
5110 int
5112  int (*proc)(Transaction *t, void *data),
5113  void *data)
5114 {
5115  if (!acc || !proc) return 0;
5116 
5118  return gnc_account_tree_staged_transaction_traversal (acc, 42, proc, data);
5119 }
5120 
5121 
5122 gint
5123 xaccAccountForEachTransaction(const Account *acc, TransactionCallback proc,
5124  void *data)
5125 {
5126  if (!acc || !proc) return 0;
5128  return xaccAccountStagedTransactionTraversal(acc, 42, proc, data);
5129 }
5130 
5131 /* ================================================================ */
5132 /* The following functions are used by
5133  * src/import-export/import-backend.c to manipulate the contra-account
5134  * matching data. See src/import-export/import-backend.c for explanations.
5135  */
5136 
5137 #define IMAP_FRAME "import-map"
5138 #define IMAP_FRAME_BAYES "import-map-bayes"
5139 
5140 /* Look up an Account in the map */
5141 Account*
5142 gnc_account_imap_find_account (Account *acc,
5143  const char *category,
5144  const char *key)
5145 {
5146  if (!acc || !key) return nullptr;
5147  std::vector<std::string> path {IMAP_FRAME};
5148  if (category)
5149  path.push_back (category);
5150  path.push_back (key);
5151  return get_kvp_account_path (acc, path);
5152 }
5153 
5154 Account*
5155 gnc_account_imap_find_any (QofBook *book, const char* category, const char *key)
5156 {
5157  Account *account = nullptr;
5158 
5159  /* Get list of Accounts */
5160  auto root = gnc_book_get_root_account (book);
5161  auto accts = gnc_account_get_descendants_sorted (root);
5162 
5163  /* Go through list of accounts */
5164  for (auto ptr = accts; ptr; ptr = g_list_next (ptr))
5165  {
5166  auto tmp_acc = static_cast<Account*> (ptr->data);
5167 
5168  if (gnc_account_imap_find_account (tmp_acc, category, key))
5169  {
5170  account = tmp_acc;
5171  break;
5172  }
5173  }
5174  g_list_free (accts);
5175 
5176 return account;
5177 }
5178 
5179 /* Store an Account in the map */
5180 void
5181 gnc_account_imap_add_account (Account *acc,
5182  const char *category,
5183  const char *key,
5184  Account *added_acc)
5185 {
5186  if (!acc || !key || !added_acc || !*key) return;
5187 
5188  auto path = category ? Path{IMAP_FRAME, category, key} : Path{IMAP_FRAME, key};
5189 
5190  set_kvp_account_path (acc, path, added_acc);
5191 }
5192 
5193 /* Remove a reference to an Account in the map */
5194 void
5195 gnc_account_imap_delete_account (Account *acc,
5196  const char *category,
5197  const char *key)
5198 {
5199  if (!acc || !key) return;
5200 
5201  auto path = category ? Path{IMAP_FRAME, category, key} : Path{IMAP_FRAME, key};
5202  if (qof_instance_has_path_slot (QOF_INSTANCE (acc), path))
5203  {
5204  qof_instance_slot_path_delete (QOF_INSTANCE (acc), path);
5205  if (category)
5206  qof_instance_slot_path_delete_if_empty (QOF_INSTANCE (acc), {IMAP_FRAME, category});
5207  qof_instance_slot_path_delete_if_empty (QOF_INSTANCE (acc), {IMAP_FRAME});
5208  }
5209  qof_instance_set_dirty (QOF_INSTANCE (acc));
5210 }
5211 
5212 /*--------------------------------------------------------------------------
5213  Below here is the bayes transaction to account matching system
5214 --------------------------------------------------------------------------*/
5215 
5216 
5222 {
5223  double product; /* product of probabilities */
5224  double product_difference; /* product of (1-probabilities) */
5225 };
5226 
5228 {
5229  std::string account_guid;
5230  int64_t token_count;
5231 };
5232 
5237 {
5238  std::vector<AccountTokenCount> accounts;
5239  int64_t total_count;
5240 };
5241 
5246 {
5247  std::string account_guid;
5248  int32_t probability;
5249 };
5250 
5251 static void
5252 build_token_info(char const * suffix, KvpValue * value, TokenAccountsInfo & tokenInfo)
5253 {
5254  if (strlen(suffix) == GUID_ENCODING_LENGTH)
5255  {
5256  tokenInfo.total_count += value->get<int64_t>();
5257  /*By convention, the key ends with the account GUID.*/
5258  tokenInfo.accounts.emplace_back(AccountTokenCount{std::string{suffix}, value->get<int64_t>()});
5259  }
5260 }
5261 
5265 static constexpr int probability_factor = 100000;
5266 
5267 static FinalProbabilityVec
5268 build_probabilities(ProbabilityVec const & first_pass)
5269 {
5270  FinalProbabilityVec ret;
5271  for (auto const & first_pass_prob : first_pass)
5272  {
5273  auto const & account_probability = first_pass_prob.second;
5274  /* P(AB) = A*B / [A*B + (1-A)*(1-B)]
5275  * NOTE: so we only keep track of a running product(A*B*C...)
5276  * and product difference ((1-A)(1-B)...)
5277  */
5278  int32_t probability = (account_probability.product /
5279  (account_probability.product + account_probability.product_difference)) * probability_factor;
5280  ret.push_back({first_pass_prob.first, probability});
5281  }
5282  return ret;
5283 }
5284 
5285 static AccountInfo
5286 highest_probability(FinalProbabilityVec const & probabilities)
5287 {
5288  AccountInfo ret {"", std::numeric_limits<int32_t>::min()};
5289  for (auto const & prob : probabilities)
5290  if (prob.second > ret.probability)
5291  ret = AccountInfo {prob.first, prob.second};
5292  return ret;
5293 }
5294 
5295 static ProbabilityVec
5296 get_first_pass_probabilities(Account* acc, GList * tokens)
5297 {
5298  ProbabilityVec ret;
5299  /* find the probability for each account that contains any of the tokens
5300  * in the input tokens list. */
5301  for (auto current_token = tokens; current_token; current_token = current_token->next)
5302  {
5303  TokenAccountsInfo tokenInfo{};
5304  auto path = std::string{IMAP_FRAME_BAYES "/"} + static_cast <char const *> (current_token->data) + "/";
5305  qof_instance_foreach_slot_prefix (QOF_INSTANCE (acc), path, &build_token_info, tokenInfo);
5306  for (auto const & current_account_token : tokenInfo.accounts)
5307  {
5308  auto item = std::find_if(ret.begin(), ret.end(), [&current_account_token]
5309  (std::pair<std::string, AccountProbability> const & a) {
5310  return current_account_token.account_guid == a.first;
5311  });
5312  if (item != ret.end())
5313  {/* This account is already in the map */
5314  item->second.product = ((double)current_account_token.token_count /
5315  (double)tokenInfo.total_count) * item->second.product;
5316  item->second.product_difference = ((double)1 - ((double)current_account_token.token_count /
5317  (double)tokenInfo.total_count)) * item->second.product_difference;
5318  }
5319  else
5320  {
5321  /* add a new entry */
5322  AccountProbability new_probability;
5323  new_probability.product = ((double)current_account_token.token_count /
5324  (double)tokenInfo.total_count);
5325  new_probability.product_difference = 1 - (new_probability.product);
5326  ret.push_back({current_account_token.account_guid, std::move(new_probability)});
5327  }
5328  } /* for all accounts in tokenInfo */
5329  }
5330  return ret;
5331 }
5332 
5333 static std::string
5334 look_for_old_separator_descendants (Account *root, std::string const & full_name, const gchar *separator)
5335 {
5336  GList *top_accounts, *ptr;
5337  gint found_len = 0;
5338  gchar found_sep;
5339  top_accounts = gnc_account_get_descendants (root);
5340  PINFO("Incoming full_name is '%s', current separator is '%s'", full_name.c_str (), separator);
5341  /* Go through list of top level accounts */
5342  for (ptr = top_accounts; ptr; ptr = g_list_next (ptr))
5343  {
5344  const gchar *name = xaccAccountGetName (static_cast <Account const *> (ptr->data));
5345  // we are looking for the longest top level account that matches
5346  if (g_str_has_prefix (full_name.c_str (), name))
5347  {
5348  gint name_len = strlen (name);
5349  const gchar old_sep = full_name[name_len];
5350  if (!g_ascii_isalnum (old_sep)) // test for non alpha numeric
5351  {
5352  if (name_len > found_len)
5353  {
5354  found_sep = full_name[name_len];
5355  found_len = name_len;
5356  }
5357  }
5358  }
5359  }
5360  g_list_free (top_accounts); // Free the List
5361  std::string new_name {full_name};
5362  if (found_len > 1)
5363  std::replace (new_name.begin (), new_name.end (), found_sep, *separator);
5364  PINFO ("Return full_name is '%s'", new_name.c_str ());
5365  return new_name;
5366 }
5367 
5368 static std::string
5369 get_guid_from_account_name (Account * root, std::string const & name)
5370 {
5371  auto map_account = gnc_account_lookup_by_full_name (root, name.c_str ());
5372  if (!map_account)
5373  {
5374  auto temp_account_name = look_for_old_separator_descendants (root, name,
5376  map_account = gnc_account_lookup_by_full_name (root, temp_account_name.c_str ());
5377  }
5378  auto temp_guid = gnc::GUID {*xaccAccountGetGUID (map_account)};
5379  return temp_guid.to_string ();
5380 }
5381 
5382 static FlatKvpEntry
5383 convert_entry (KvpEntry entry, Account* root)
5384 {
5385  /*We need to make a copy here.*/
5386  auto account_name = entry.first.back();
5387  if (!gnc::GUID::is_valid_guid (account_name))
5388  {
5389  /* Earlier version stored the account name in the import map, and
5390  * there were early beta versions of 2.7 that stored a GUID.
5391  * If there is no GUID, we assume it's an account name. */
5392  /* Take off the account name and replace it with the GUID */
5393  entry.first.pop_back();
5394  auto guid_str = get_guid_from_account_name (root, account_name);
5395  entry.first.emplace_back (guid_str);
5396  }
5397  std::string new_key {std::accumulate (entry.first.begin(), entry.first.end(), std::string {})};
5398  new_key = IMAP_FRAME_BAYES + new_key;
5399  return {new_key, entry.second};
5400 }
5401 
5402 static std::vector<FlatKvpEntry>
5403 get_flat_imap (Account * acc)
5404 {
5405  auto frame = qof_instance_get_slots (QOF_INSTANCE (acc));
5406  auto slot = frame->get_slot ({IMAP_FRAME_BAYES});
5407  if (!slot)
5408  return {};
5409  auto imap_frame = slot->get<KvpFrame*> ();
5410  auto flat_kvp = imap_frame->flatten_kvp ();
5411  auto root = gnc_account_get_root (acc);
5412  std::vector <FlatKvpEntry> ret;
5413  for (auto const & flat_entry : flat_kvp)
5414  {
5415  auto converted_entry = convert_entry (flat_entry, root);
5416  /*If the entry was invalid, we don't perpetuate it.*/
5417  if (converted_entry.first.size())
5418  ret.emplace_back (converted_entry);
5419  }
5420  return ret;
5421 }
5422 
5423 static bool
5424 convert_imap_account_bayes_to_flat (Account *acc)
5425 {
5426  auto frame = qof_instance_get_slots (QOF_INSTANCE (acc));
5427  if (!frame->get_keys().size())
5428  return false;
5429  auto flat_imap = get_flat_imap(acc);
5430  if (!flat_imap.size ())
5431  return false;
5432  xaccAccountBeginEdit(acc);
5433  frame->set({IMAP_FRAME_BAYES}, nullptr);
5434  std::for_each(flat_imap.begin(), flat_imap.end(),
5435  [&frame] (FlatKvpEntry const & entry) {
5436  frame->set({entry.first.c_str()}, entry.second);
5437  });
5438  qof_instance_set_dirty (QOF_INSTANCE (acc));
5439  xaccAccountCommitEdit(acc);
5440  return true;
5441 }
5442 
5443 /*
5444  * Checks for import map data and converts them when found.
5445  */
5446 static bool
5447 imap_convert_bayes_to_flat (QofBook * book)
5448 {
5449  auto root = gnc_book_get_root_account (book);
5450  auto accts = gnc_account_get_descendants_sorted (root);
5451  bool ret = false;
5452  for (auto ptr = accts; ptr; ptr = g_list_next (ptr))
5453  {
5454  Account *acc = static_cast <Account*> (ptr->data);
5455  if (convert_imap_account_bayes_to_flat (acc))
5456  {
5457  ret = true;
5458  gnc_features_set_used (book, GNC_FEATURE_GUID_FLAT_BAYESIAN);
5459  }
5460  }
5461  g_list_free (accts);
5462  return ret;
5463 }
5464 
5465 void
5467 {
5468  imap_convert_bayes_to_flat_run = false;
5469 }
5470 
5471 /*
5472  * Here we check to see the state of import map data.
5473  *
5474  * If the GUID_FLAT_BAYESIAN feature flag is set, everything
5475  * should be fine.
5476  *
5477  * If it is not set, there are two possibilities: import data
5478  * are present from a previous version or not. If they are,
5479  * they are converted, and the feature flag set. If there are
5480  * no previous data, nothing is done.
5481  */
5482 static void
5483 check_import_map_data (QofBook *book)
5484 {
5485  if (gnc_features_check_used (book, GNC_FEATURE_GUID_FLAT_BAYESIAN) ||
5486  imap_convert_bayes_to_flat_run)
5487  return;
5488 
5489  /* This function will set GNC_FEATURE_GUID_FLAT_BAYESIAN if necessary.*/
5490  imap_convert_bayes_to_flat (book);
5491  imap_convert_bayes_to_flat_run = true;
5492 }
5493 
5494 static constexpr double threshold = .90 * probability_factor; /* 90% */
5495 
5497 Account*
5499 {
5500  if (!acc)
5501  return nullptr;
5502  auto book = gnc_account_get_book(acc);
5503  check_import_map_data (book);
5504  auto first_pass = get_first_pass_probabilities(acc, tokens);
5505  if (!first_pass.size())
5506  return nullptr;
5507  auto final_probabilities = build_probabilities(first_pass);
5508  if (!final_probabilities.size())
5509  return nullptr;
5510  auto best = highest_probability(final_probabilities);
5511  if (best.account_guid == "")
5512  return nullptr;
5513  if (best.probability < threshold)
5514  return nullptr;
5515  gnc::GUID guid;
5516  try {
5517  guid = gnc::GUID::from_string(best.account_guid);
5518  } catch (gnc::guid_syntax_exception&) {
5519  return nullptr;
5520  }
5521  auto account = xaccAccountLookup (reinterpret_cast<GncGUID*>(&guid), book);
5522  return account;
5523 }
5524 
5525 static void
5526 change_imap_entry (Account *acc, std::string const & path, int64_t token_count)
5527 {
5528  PINFO("Source Account is '%s', Count is '%" G_GINT64_FORMAT "'",
5529  xaccAccountGetName (acc), token_count);
5530 
5531  // check for existing guid entry
5532  if (auto existing_token_count = get_kvp_int64_path (acc, {path}))
5533  {
5534  PINFO("found existing value of '%" G_GINT64_FORMAT "'", *existing_token_count);
5535  token_count += *existing_token_count;
5536  }
5537 
5538  // Add or Update the entry based on guid
5539  set_kvp_int64_path (acc, {path}, token_count);
5540 }
5541 
5543 void
5545  GList *tokens,
5546  Account *added_acc)
5547 {
5548  GList *current_token;
5549  gint64 token_count;
5550  char *account_fullname;
5551  char *guid_string;
5552 
5553  ENTER(" ");
5554  if (!acc)
5555  {
5556  LEAVE(" ");
5557  return;
5558  }
5559  check_import_map_data (gnc_account_get_book(acc));
5560 
5561  g_return_if_fail (added_acc != nullptr);
5562  account_fullname = gnc_account_get_full_name(added_acc);
5563  xaccAccountBeginEdit (acc);
5564 
5565  PINFO("account name: '%s'", account_fullname);
5566 
5567  guid_string = guid_to_string (xaccAccountGetGUID (added_acc));
5568 
5569  /* process each token in the list */
5570  for (current_token = g_list_first(tokens); current_token;
5571  current_token = current_token->next)
5572  {
5573  char* token = static_cast<char*>(current_token->data);
5574  /* Jump to next iteration if the pointer is not valid or if the
5575  string is empty. In HBCI import we almost always get an empty
5576  string, which doesn't work in the kvp loopkup later. So we
5577  skip this case here. */
5578  if (!token || !token[0])
5579  continue;
5580  /* start off with one token for this account */
5581  token_count = 1;
5582  PINFO("adding token '%s'", token);
5583  auto path = std::string {IMAP_FRAME_BAYES} + '/' + token + '/' + guid_string;
5584  /* change the imap entry for the account */
5585  change_imap_entry (acc, path, token_count);
5586  }
5587  /* free up the account fullname and guid string */
5588  xaccAccountCommitEdit (acc);
5589  gnc_features_set_used (gnc_account_get_book(acc), GNC_FEATURE_GUID_FLAT_BAYESIAN);
5590  g_free (account_fullname);
5591  g_free (guid_string);
5592  LEAVE(" ");
5593 }
5594 
5595 /*******************************************************************************/
5596 
5597 static void
5598 build_non_bayes (const char *key, const GValue *value, gpointer user_data)
5599 {
5600  if (!G_VALUE_HOLDS_BOXED (value))
5601  return;
5602  QofBook *book;
5603  GncGUID *guid = nullptr;
5604  gchar *guid_string = nullptr;
5605  auto imapInfo = (GncImapInfo*)user_data;
5606  // Get the book
5607  book = qof_instance_get_book (imapInfo->source_account);
5608 
5609  guid = (GncGUID*)g_value_get_boxed (value);
5610  guid_string = guid_to_string (guid);
5611 
5612  PINFO("build_non_bayes: match string '%s', match account guid: '%s'",
5613  (char*)key, guid_string);
5614 
5615  auto imapInfo_node = static_cast <GncImapInfo*> (g_malloc(sizeof(GncImapInfo)));
5616 
5617  imapInfo_node->source_account = imapInfo->source_account;
5618  imapInfo_node->map_account = xaccAccountLookup (guid, book);
5619  imapInfo_node->head = g_strdup (imapInfo->head);
5620  imapInfo_node->match_string = g_strdup (key);
5621  imapInfo_node->category = g_strdup (imapInfo->category);
5622  imapInfo_node->count = g_strdup (" ");
5623 
5624  imapInfo->list = g_list_prepend (imapInfo->list, imapInfo_node);
5625 
5626  g_free (guid_string);
5627 }
5628 
5629 static void
5630 build_bayes (const char *suffix, KvpValue * value, GncImapInfo & imapInfo)
5631 {
5632  size_t guid_start = strlen(suffix) - GUID_ENCODING_LENGTH;
5633  std::string account_guid {&suffix[guid_start]};
5634  GncGUID guid;
5635  try
5636  {
5637  guid = gnc::GUID::from_string (account_guid);
5638  }
5639  catch (const gnc::guid_syntax_exception& err)
5640  {
5641  PWARN("Invalid GUID string from %s%s", IMAP_FRAME_BAYES, suffix);
5642  }
5643  auto map_account = xaccAccountLookup (&guid, gnc_account_get_book (imapInfo.source_account));
5644  auto imap_node = static_cast <GncImapInfo*> (g_malloc (sizeof (GncImapInfo)));
5645  auto count = value->get <int64_t> ();
5646  imap_node->source_account = imapInfo.source_account;
5647  imap_node->map_account = map_account;
5648  imap_node->head = g_strdup_printf ("%s%s", IMAP_FRAME_BAYES, suffix);
5649  imap_node->match_string = g_strndup (&suffix[1], guid_start - 2);
5650  imap_node->category = g_strdup(" ");
5651  imap_node->count = g_strdup_printf ("%" G_GINT64_FORMAT, count);
5652  imapInfo.list = g_list_prepend (imapInfo.list, imap_node);
5653 }
5654 
5656 {
5657  g_free (imapInfo->head);
5658  g_free (imapInfo->category);
5659  g_free (imapInfo->match_string);
5660  g_free (imapInfo->count);
5661  g_free (imapInfo);
5662 }
5663 
5664 GList *
5666 {
5667  check_import_map_data (gnc_account_get_book (acc));
5668  /* A dummy object which is used to hold the specified account, and the list
5669  * of data about which we care. */
5670  GncImapInfo imapInfo {acc, nullptr};
5671  qof_instance_foreach_slot_prefix (QOF_INSTANCE (acc), IMAP_FRAME_BAYES, &build_bayes, imapInfo);
5672  return g_list_reverse(imapInfo.list);
5673 }
5674 
5675 GList *
5676 gnc_account_imap_get_info (Account *acc, const char *category)
5677 {
5678  GList *list = nullptr;
5679 
5680  GncImapInfo imapInfo;
5681 
5682  std::vector<std::string> path {IMAP_FRAME};
5683  if (category)
5684  path.emplace_back (category);
5685 
5686  imapInfo.source_account = acc;
5687  imapInfo.list = list;
5688 
5689  imapInfo.head = g_strdup (IMAP_FRAME);
5690  imapInfo.category = g_strdup (category);
5691 
5692  if (qof_instance_has_path_slot (QOF_INSTANCE (acc), path))
5693  {
5694  qof_instance_foreach_slot (QOF_INSTANCE(acc), IMAP_FRAME, category,
5695  build_non_bayes, &imapInfo);
5696  }
5697  g_free (imapInfo.head);
5698  g_free (imapInfo.category);
5699  return g_list_reverse(imapInfo.list);
5700 }
5701 
5702 /*******************************************************************************/
5703 
5704 gchar *
5705 gnc_account_get_map_entry (Account *acc, const char *head, const char *category)
5706 {
5707  return g_strdup (category ?
5708  get_kvp_string_path (acc, {head, category}) :
5709  get_kvp_string_path (acc, {head}));
5710 }
5711 
5712 GncGUID *
5713 gnc_account_get_map_guid_entry (Account *acc, const char *head, const char *category)
5714 {
5715  return category ?
5716  get_kvp_guid_path (acc, {head, category}) :
5717  get_kvp_guid_path (acc, {head});
5718 }
5719 
5720 void
5721 gnc_account_delete_map_entry (Account *acc, char *head, char *category,
5722  char *match_string, gboolean empty)
5723 {
5724  if (acc != nullptr)
5725  {
5726  std::vector<std::string> path {head};
5727  if (category)
5728  path.emplace_back (category);
5729  if (match_string)
5730  path.emplace_back (match_string);
5731 
5732  if (qof_instance_has_path_slot (QOF_INSTANCE (acc), path))
5733  {
5734  xaccAccountBeginEdit (acc);
5735  if (empty)
5736  qof_instance_slot_path_delete_if_empty (QOF_INSTANCE(acc), path);
5737  else
5738  qof_instance_slot_path_delete (QOF_INSTANCE(acc), path);
5739  PINFO("Account is '%s', head is '%s', category is '%s', match_string is'%s'",
5740  xaccAccountGetName (acc), head, category, match_string);
5741  qof_instance_set_dirty (QOF_INSTANCE(acc));
5742  xaccAccountCommitEdit (acc);
5743  }
5744  }
5745 }
5746 
5747 void
5749 {
5750  if (acc != nullptr)
5751  {
5752  auto slots = qof_instance_get_slots_prefix (QOF_INSTANCE (acc), IMAP_FRAME_BAYES);
5753  if (!slots.size()) return;
5754  xaccAccountBeginEdit (acc);
5755  for (auto const & entry : slots)
5756  {
5757  qof_instance_slot_path_delete (QOF_INSTANCE (acc), {entry.first});
5758  }
5759  qof_instance_set_dirty (QOF_INSTANCE(acc));
5760  xaccAccountCommitEdit (acc);
5761  }
5762 }
5763 
5764 /* ================================================================ */
5765 /* QofObject function implementation and registration */
5766 
5767 static void
5768 destroy_all_child_accounts (Account *acc, gpointer data)
5769 {
5770  xaccAccountBeginEdit (acc);
5771  xaccAccountDestroy (acc);
5772 }
5773 
5774 static void
5775 gnc_account_book_end(QofBook* book)
5776 {
5777  Account *root_account = gnc_book_get_root_account (book);
5778  GList *accounts;
5779 
5780  if (!root_account)
5781  return;
5782 
5783  accounts = gnc_account_get_descendants (root_account);
5784 
5785  if (accounts)
5786  {
5787  accounts = g_list_reverse (accounts);
5788  g_list_foreach (accounts, (GFunc)destroy_all_child_accounts, nullptr);
5789  g_list_free (accounts);
5790  }
5791  xaccAccountBeginEdit (root_account);
5792  xaccAccountDestroy (root_account);
5793 }
5794 
5795 #ifdef _MSC_VER
5796 /* MSVC compiler doesn't have C99 "designated initializers"
5797  * so we wrap them in a macro that is empty on MSVC. */
5798 # define DI(x) /* */
5799 #else
5800 # define DI(x) x
5801 #endif
5802 static QofObject account_object_def =
5803 {
5804  DI(.interface_version = ) QOF_OBJECT_VERSION,
5805  DI(.e_type = ) GNC_ID_ACCOUNT,
5806  DI(.type_label = ) "Account",
5807  DI(.create = ) (void*(*)(QofBook*)) xaccMallocAccount,
5808  DI(.book_begin = ) nullptr,
5809  DI(.book_end = ) gnc_account_book_end,
5810  DI(.is_dirty = ) qof_collection_is_dirty,
5811  DI(.mark_clean = ) qof_collection_mark_clean,
5812  DI(.foreach = ) qof_collection_foreach,
5813  DI(.printable = ) (const char * (*)(gpointer)) xaccAccountGetName,
5814  DI(.version_cmp = ) (int (*)(gpointer, gpointer)) qof_instance_version_cmp,
5815 };
5816 
5817 gboolean xaccAccountRegister (void)
5818 {
5819  static QofParam params[] =
5820  {
5821  {
5822  ACCOUNT_NAME_, QOF_TYPE_STRING,
5825  },
5826  {
5827  ACCOUNT_CODE_, QOF_TYPE_STRING,
5830  },
5831  {
5832  ACCOUNT_DESCRIPTION_, QOF_TYPE_STRING,
5835  },
5836  {
5837  ACCOUNT_COLOR_, QOF_TYPE_STRING,
5840  },
5841  {
5842  ACCOUNT_FILTER_, QOF_TYPE_STRING,
5845  },
5846  {
5847  ACCOUNT_SORT_ORDER_, QOF_TYPE_STRING,
5850  },
5851  {
5852  ACCOUNT_SORT_REVERSED_, QOF_TYPE_BOOLEAN,
5855  },
5856  {
5857  ACCOUNT_NOTES_, QOF_TYPE_STRING,
5860  },
5861  {
5862  ACCOUNT_PRESENT_, QOF_TYPE_NUMERIC,
5863  (QofAccessFunc) xaccAccountGetPresentBalance, nullptr
5864  },
5865  {
5866  ACCOUNT_BALANCE_, QOF_TYPE_NUMERIC,
5868  },
5869  {
5870  ACCOUNT_CLEARED_, QOF_TYPE_NUMERIC,
5872  },
5873  {
5874  ACCOUNT_RECONCILED_, QOF_TYPE_NUMERIC,
5876  },
5877  {
5878  ACCOUNT_TYPE_, QOF_TYPE_STRING,
5879  (QofAccessFunc) qofAccountGetTypeString,
5880  (QofSetterFunc) qofAccountSetType
5881  },
5882  {
5883  ACCOUNT_FUTURE_MINIMUM_, QOF_TYPE_NUMERIC,
5884  (QofAccessFunc) xaccAccountGetProjectedMinimumBalance, nullptr
5885  },
5886  {
5887  ACCOUNT_TAX_RELATED, QOF_TYPE_BOOLEAN,
5890  },
5891  {
5892  ACCOUNT_OPENING_BALANCE_, QOF_TYPE_BOOLEAN,
5895  },
5896  {
5897  ACCOUNT_SCU, QOF_TYPE_INT32,
5900  },
5901  {
5902  ACCOUNT_NSCU, QOF_TYPE_BOOLEAN,
5905  },
5906  {
5907  ACCOUNT_PARENT, GNC_ID_ACCOUNT,
5909  (QofSetterFunc) qofAccountSetParent
5910  },
5911  {
5912  QOF_PARAM_BOOK, QOF_ID_BOOK,
5914  },
5915  {
5916  QOF_PARAM_GUID, QOF_TYPE_GUID,
5918  },
5919  { nullptr },
5920  };
5921 
5922  qof_class_register (GNC_ID_ACCOUNT, (QofSortFunc) qof_xaccAccountOrder, params);
5923 
5924  return qof_object_register (&account_object_def);
5925 }
5926 
5927 /* ======================= UNIT TESTING ACCESS =======================
5928  * The following functions are for unit testing use only.
5929  */
5930 static AccountPrivate*
5931 utest_account_get_private (Account *acc)
5932 {
5933  return GET_PRIVATE (acc);
5934 }
5935 
5937 _utest_account_fill_functions(void)
5938 {
5939  AccountTestFunctions* func = g_new(AccountTestFunctions, 1);
5940 
5941  func->get_private = utest_account_get_private;
5942  func->coll_get_root_account = gnc_coll_get_root_account;
5943  func->xaccFreeAccountChildren = xaccFreeAccountChildren;
5944  func->xaccFreeAccount = xaccFreeAccount;
5945  func->qofAccountSetParent = qofAccountSetParent;
5946  func->gnc_account_lookup_by_full_name_helper =
5947  gnc_account_lookup_by_full_name_helper;
5948 
5949  return func;
5950 }
5951 /* ======================= 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:2916
void xaccAccountSetFilter(Account *acc, const char *str)
Set the account&#39;s Filter.
Definition: Account.cpp:2600
void xaccAccountSetSortOrder(Account *acc, const char *str)
Set the account&#39;s Sort Order.
Definition: Account.cpp:2606
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:5123
int xaccAccountTreeForEachTransaction(Account *acc, TransactionCallback proc, void *data)
Traverse all of the transactions in the given account group.
gint xaccSplitOrder(const Split *sa, const Split *sb)
The xaccSplitOrder(sa,sb) method is useful for sorting.
Definition: Split.cpp:1501
This is the private header for the account structure.
gnc_commodity_table * gnc_commodity_table_get_table(QofBook *book)
Returns the commodity table associated with a book.
gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
Equivalence predicate: Returns TRUE (1) if a and b represent the same number.
int gnc_account_tree_staged_transaction_traversal(const Account *acc, unsigned int stage, TransactionCallback thunk, void *cb_data)
gnc_account_tree_staged_transaction_traversal() calls thunk on each transaction in the group whose cu...
Definition: Account.cpp:5062
gboolean xaccAccountGetAutoInterest(const Account *acc)
Get the "auto interest" flag for an account.
Definition: Account.cpp:4148
holds an account guid and its corresponding integer probability the integer probability is some facto...
Definition: Account.cpp:5245
const char * xaccAccountGetLastNum(const Account *acc)
Get the last num field of an Account.
Definition: Account.cpp:4656
GNCAccountType xaccAccountTypeGetFundamental(GNCAccountType t)
Convenience function to return the fundamental type asset/liability/income/expense/equity given an ac...
Definition: Account.cpp:4468
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:5705
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:3349
guint32 xaccAccountTypesCompatibleWith(GNCAccountType type)
Return the bitmask of account types compatible with a given type.
Definition: Account.cpp:4340
void gnc_account_imap_info_destroy(GncImapInfo *imapInfo)
Clean destructor for the imap_info structure of Bayesian mappings.
Definition: Account.cpp:5655
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:2817
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:3033
QOF event handling interface.
gint gnc_account_n_descendants(const Account *account)
Return the number of descendants of the specified account.
Definition: Account.cpp:2983
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:4044
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:2934
SplitList * xaccAccountGetSplitList(const Account *acc)
The xaccAccountGetSplitList() routine returns a pointer to a GList of the splits in the account...
Definition: Account.cpp:3923
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:2650
gboolean xaccAccountGetNonStdSCU(const Account *acc)
Return boolean, indicating whether this account uses a non-standard SCU.
Definition: Account.cpp:2755
int xaccAccountGetCommoditySCUi(const Account *acc)
Return the &#39;internal&#39; SCU setting.
Definition: Account.cpp:2719
#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:3377
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:3217
void xaccAccountSetNotes(Account *acc, const char *str)
Set the account&#39;s notes.
Definition: Account.cpp:2636
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:4524
gboolean qof_collection_is_dirty(const QofCollection *col)
Return value of &#39;dirty&#39; flag on collection.
Definition: qofid.cpp:232
void gnc_account_delete_map_entry(Account *acc, char *head, char *category, char *match_string, gboolean empty)
Delete the entry for Account pointed to by head,category and match_string, if empty is TRUE then use ...
Definition: Account.cpp:5721
gboolean xaccTransIsOpen(const Transaction *trans)
The xaccTransIsOpen() method returns TRUE if the transaction is open for editing. ...
a simple price database for gnucash
QofInstance * qof_collection_lookup_entity(const QofCollection *col, const GncGUID *guid)
Find the entity going only from its guid.
Definition: qofid.cpp:209
Expense accounts are used to denote expenses.
Definition: Account.h:143
int safe_utf8_collate(const char *da, const char *db)
Collate two UTF-8 strings.
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
const char * xaccAccountGetFilter(const Account *acc)
Get the account&#39;s filter.
Definition: Account.cpp:3337
gnc_numeric xaccSplitGetReconciledBalance(const Split *s)
Returns the reconciled-balance of this split.
Definition: Split.cpp:1314
GNCAccountType xaccAccountGetType(const Account *acc)
Returns the account&#39;s account type.
Definition: Account.cpp:3248
gboolean xaccSplitDestroy(Split *split)
Destructor.
Definition: Split.cpp:1471
void xaccAccountSetMark(Account *acc, short m)
Set a mark on the account.
Definition: Account.cpp:2060
const char * xaccAccountGetOnlineID(const Account *acc)
Get the account&#39;s online_id (see xaccAccountSetOnlineID).
Definition: Account.cpp:3361
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:2726
const char * xaccAccountGetCode(const Account *acc)
Get the account&#39;s accounting code.
Definition: Account.cpp:3317
gboolean xaccAccountGetAppendText(const Account *acc)
Get the "import-append-text" flag for an account.
Definition: Account.cpp:4104
void xaccAccountSetReconcileLastDate(Account *acc, time64 last_date)
DOCUMENT ME!
Definition: Account.cpp:4557
STRUCTS.
total_count and the token_count for a given account let us calculate the probability of a given accou...
Definition: Account.cpp:5236
Account * gnc_account_create_root(QofBook *book)
Create a new root level account.
Definition: Account.cpp:1283
void gnc_commodity_decrement_usage_count(gnc_commodity *cm)
Decrement a commodity&#39;s internal counter that tracks how many accounts are using that commodity...
GncGUID * guid_copy(const GncGUID *guid)
Returns a newly allocated GncGUID that matches the passed-in GUID.
Definition: guid.cpp:155
void xaccAccountSetTaxRelated(Account *acc, gboolean tax_related)
DOCUMENT ME!
Definition: Account.cpp:4014
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:3225
void gnc_account_set_policy(Account *acc, GNCPolicy *policy)
Set the account&#39;s lot order policy.
Definition: Account.cpp:2106
void xaccAccountSetOnlineID(Account *acc, const char *id)
Set the account&#39;s online_id, the identifier (e.g.
Definition: Account.cpp:2642
void xaccAccountSetReconcileLastInterval(Account *acc, int months, int days)
DOCUMENT ME!
Definition: Account.cpp:4587
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:3825
guint32 xaccAccountTypesValid(void)
Returns the bitmask of the account type enums that are valid.
Definition: Account.cpp:4445
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:4220
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:4706
void xaccAccountInsertLot(Account *acc, GNCLot *lot)
The xaccAccountInsertLot() method will register the indicated lot with this account.
Definition: Account.cpp:2138
Transaction * xaccSplitGetParent(const Split *split)
Returns the parent transaction of the split.
API for Transactions and Splits (journal entries)
gchar * guid_to_string_buff(const GncGUID *guid, gchar *str)
The guid_to_string_buff() routine puts a null-terminated string encoding of the id into the memory po...
Definition: guid.cpp:208
int(* QofSortFunc)(gconstpointer, gconstpointer)
This function is the default sort function for a particular object type.
Definition: qofclass.h:168
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:4064
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:5544
gnc_numeric xaccSplitGetBalance(const Split *s)
Returns the running balance up to and including the indicated split.
Definition: Split.cpp:1296
#define QOF_PARAM_BOOK
"Known" Object Parameters – all objects must support these
Definition: qofquery.h:108
void xaccAccountSetLastNum(Account *acc, const char *num)
Set the last num field of an Account.
Definition: Account.cpp:4665
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:3455
void(* QofSetterFunc)(gpointer, gpointer)
The QofSetterFunc defines an function pointer for parameter setters.
Definition: qofclass.h:130
GNCPriceDB * gnc_pricedb_get_db(QofBook *book)
Return the pricedb associated with the book.
gnc_numeric gnc_pricedb_convert_balance_latest_price(GNCPriceDB *pdb, gnc_numeric balance, const gnc_commodity *balance_currency, const gnc_commodity *new_currency)
Convert a balance from one currency to another using the most recent price between the two...
gboolean xaccAccountGetReconcilePostponeDate(const Account *acc, time64 *postpone_date)
DOCUMENT ME!
Definition: Account.cpp:4597
intermediate values used to calculate the bayes probability of a given account where p(AB) = (a*b)/[a...
Definition: Account.cpp:5221
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:4699
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:4175
gboolean xaccSplitEqual(const Split *sa, const Split *sb, gboolean check_guids, gboolean check_balances, gboolean check_txn_splits)
Equality.
Definition: Split.cpp:803
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:3074
#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:2879
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:3331
Split * xaccAccountFindSplitByDesc(const Account *acc, const char *description)
Returns a pointer to the split, not a copy.
Definition: Account.cpp:4881
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:4454
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:4291
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:3286
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:3519
void xaccAccountSetPlaceholder(Account *acc, gboolean val)
Set the "placeholder" flag for an account.
Definition: Account.cpp:4098
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:4427
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:3835
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:4724
gboolean xaccTransactionTraverse(Transaction *trans, int stage)
xaccTransactionTraverse() checks the stage of the given transaction.
Definition: Account.cpp:5012
void xaccAccountSetColor(Account *acc, const char *str)
Set the account&#39;s Color.
Definition: Account.cpp:2594
Transaction * xaccAccountFindTransByDesc(const Account *acc, const char *description)
Returns a pointer to the transaction, not a copy.
Definition: Account.cpp:4894
void xaccAccountSetIncludeSubAccountBalances(Account *acc, gboolean inc_sub)
Set whether to include balances of sub accounts.
Definition: Account.cpp:4736
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:3206
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:3087
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:5027
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:4808
#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:5004
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:4135
const char * xaccAccountGetDescription(const Account *acc)
Get the account&#39;s description.
Definition: Account.cpp:3324
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:3435
void gnc_account_delete_all_bayes_maps(Account *acc)
Delete all bayes entries for Account.
Definition: Account.cpp:5748
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:4844
time64 gnc_account_get_earliest_date(const Account *account)
Returns the date of the earliest split in the account, or INT64_MAX.
Definition: Account.cpp:5100
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:4647
const char * xaccAccountGetTaxUSPayerNameSource(const Account *acc)
DOCUMENT ME!
Definition: Account.cpp:4032
gnc_numeric xaccSplitGetNoclosingBalance(const Split *s)
The noclosing-balance is the currency-denominated balance of all transactions except &#39;closing&#39; transa...
Definition: Split.cpp:1302
gint null_strcmp(const gchar *da, const gchar *db)
The null_strcmp compares strings a and b the same way that strcmp() does, except that either may be n...
Definition: qofutil.cpp:123
void gnc_account_reset_convert_bayes_to_flat(void)
Reset the flag that indicates the function imap_convert_bayes_to_flat has been run.
Definition: Account.cpp:5466
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:2950
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:3948
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:5665
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:5676
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:3144
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:4612
gboolean qof_commit_edit_part2(QofInstance *inst, void(*on_error)(QofInstance *, QofBackendError), void(*on_done)(QofInstance *), void(*on_free)(QofInstance *))
part2 – deal with the backend
gpointer(* QofAccessFunc)(gpointer object, const QofParam *param)
The QofAccessFunc defines an arbitrary function pointer for access functions.
Definition: qofclass.h:123
A/P account type.
Definition: Account.h:151
const char * xaccAccountGetTaxUSCode(const Account *acc)
DOCUMENT ME!
Definition: Account.cpp:4020
gboolean xaccAccountIsAPARType(GNCAccountType t)
Convenience function to check if the account is a valid business account type (meaning an Accounts Pa...
Definition: Account.cpp:4500
void qof_collection_insert_entity(QofCollection *, QofInstance *)
Take entity, remove it from whatever collection its currently in, and place it in a new collection...
Definition: qofid.cpp:95
bank account type – don&#39;t use this for now, see NUM_ACCOUNT_TYPES
Definition: Account.h:167
void qof_collection_mark_clean(QofCollection *)
reset value of dirty flag
Definition: qofid.cpp:238
gboolean xaccAccountStringToType(const char *str, GNCAccountType *type)
Conversion routines for the account types to/from strings that are used in persistent storage...
Definition: Account.cpp:4257
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:4126
void xaccAccountSetReconcilePostponeBalance(Account *acc, gnc_numeric balance)
DOCUMENT ME!
Definition: Account.cpp:4637
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:4008
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:3422
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:3462
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:2957
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:4904
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:3507
gnc_numeric xaccAccountGetBalance(const Account *acc)
Get the current balance of the account, which may include future splits.
Definition: Account.cpp:3448
gboolean xaccAccountGetReconcileLastDate(const Account *acc, time64 *last_date)
DOCUMENT ME!
Definition: Account.cpp:4539
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:4833
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:2703
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:3396
Account * xaccAccountGetAssociatedAccount(const Account *acc, const char *tag)
Get the account&#39;s associated account e.g.
Definition: Account.cpp:3368
gboolean xaccAccountGetHidden(const Account *acc)
Get the "hidden" flag for an account.
Definition: Account.cpp:4163
void xaccAccountSetAppendText(Account *acc, gboolean val)
Set the "import-append-text" flag for an account.
Definition: Account.cpp:4110
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:3025
void xaccAccountSetAutoInterest(Account *acc, gboolean val)
Set the "auto interest" flag for an account.
Definition: Account.cpp:4154
void xaccAccountSetTaxUSCode(Account *acc, const char *code)
DOCUMENT ME!
Definition: Account.cpp:4026
GNCPlaceholderType
DOCUMENT ME!
Definition: Account.h:1278
gboolean xaccAccountGetIsOpeningBalance(const Account *acc)
Get the "opening-balance" flag for an account.
Definition: Account.cpp:4116
QofBook reference.
Definition: qofbook-p.hpp:46
guint32 xaccParentAccountTypesCompatibleWith(GNCAccountType type)
Return the bitmask of parent account types compatible with a given type.
Definition: Account.cpp:4380
gboolean xaccAccountGetReconcileChildrenStatus(const Account *acc)
DOCUMENT ME!
Definition: Account.cpp:4868
gboolean xaccAccountGetReconcileLastInterval(const Account *acc, int *months, int *days)
DOCUMENT ME!
Definition: Account.cpp:4566
gboolean xaccAccountGetIncludeSubAccountBalances(const Account *acc)
Get whether to include balances of sub accounts.
Definition: Account.cpp:4730
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:4820
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:4076
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:2941
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:4169
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:4197
Account * xaccSplitGetAccount(const Split *split)
Returns the account of this split, which was set through xaccAccountInsertSplit().
Definition: gmock-Split.cpp:53
gchar * guid_to_string(const GncGUID *guid)
The guid_to_string() routine returns a null-terminated string encoding of the id. ...
Definition: guid.cpp:199
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account&#39;s commodity.
Definition: Account.cpp:3389
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:4092
time64 gnc_time64_get_today_end(void)
The gnc_time64_get_today_end() routine returns a time64 value corresponding to the last second of tod...
Definition: gnc-date.cpp:1426
gint gnc_account_get_current_depth(const Account *account)
Return the number of levels of this account below the root account.
Definition: Account.cpp:2991
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:2612
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:4790
void xaccAccountSetLowerBalanceLimit(Account *acc, gnc_numeric balance)
Set the lower balance limit for the account.
Definition: Account.cpp:4712
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:4692
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:2974
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:2964
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:4038
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:2766
gpointer qof_collection_get_data(const QofCollection *col)
Store and retrieve arbitrary object-defined data.
Definition: qofid.cpp:266
void gnc_account_set_start_balance(Account *acc, const gnc_numeric start_baln)
This function will set the starting commodity balance for this account.
Definition: Account.cpp:3410
void xaccAccountSetNonStdSCU(Account *acc, gboolean flag)
Set the flag indicating that this account uses a non-standard SCU.
Definition: Account.cpp:2739
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:3955
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:2923
Account * gnc_account_lookup_by_opening_balance(Account *account, gnc_commodity *commodity)
Find the opening balance account for the currency.
Definition: Account.cpp:3102
void xaccAccountClearHigherBalanceLimit(Account *acc)
Clear the higher balance limit for the account.
Definition: Account.cpp:4718
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:3270
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:4330
#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:3343
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:5035
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...
GncGUID * gnc_account_get_map_guid_entry(Account *acc, const char *head, const char *category)
Returns the guid pointed to by head and category for the Account, free the returned guid...
Definition: Account.cpp:5713
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:4926
gboolean xaccAccountIsEquityType(GNCAccountType t)
Convenience function to check if the account is a valid Equity type.
Definition: Account.cpp:4512
void xaccAccountSetReconcileChildrenStatus(Account *acc, gboolean status)
DOCUMENT ME!
Definition: Account.cpp:4855
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:2659
#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:3169
const char * xaccAccountGetNotes(const Account *acc)
Get the account&#39;s notes.
Definition: Account.cpp:3355
gboolean xaccAccountGetReconcilePostponeBalance(const Account *acc, gnc_numeric *balance)
DOCUMENT ME!
Definition: Account.cpp:4621
gint gnc_account_get_tree_depth(const Account *account)
Return the number of levels of descendants accounts below the specified account.
Definition: Account.cpp:3010
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:5498
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:4051