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