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