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