GnuCash  4.11-354-g0815ab64c1
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 
4015 gboolean gnc_account_and_descendants_empty (Account *acc)
4016 {
4017  g_return_val_if_fail (GNC_IS_ACCOUNT (acc), FALSE);
4018  auto priv = GET_PRIVATE (acc);
4019  if (priv->splits != nullptr) return FALSE;
4020  for (auto *n = priv->children; n; n = n->next)
4021  {
4022  if (!gnc_account_and_descendants_empty (static_cast<Account*>(n->data)))
4023  return FALSE;
4024  }
4025  return TRUE;
4026 }
4027 
4028 LotList *
4030 {
4031  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), NULL);
4032  return g_list_copy(GET_PRIVATE(acc)->lots);
4033 }
4034 
4035 LotList *
4037  gboolean (*match_func)(GNCLot *lot,
4038  gpointer user_data),
4039  gpointer user_data, GCompareFunc sort_func)
4040 {
4041  AccountPrivate *priv;
4042  GList *lot_list;
4043  GList *retval = NULL;
4044 
4045  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), NULL);
4046 
4047  priv = GET_PRIVATE(acc);
4048  for (lot_list = priv->lots; lot_list; lot_list = lot_list->next)
4049  {
4050  GNCLot *lot = static_cast<GNCLot*>(lot_list->data);
4051 
4052  /* If this lot is closed, then ignore it */
4053  if (gnc_lot_is_closed (lot))
4054  continue;
4055 
4056  if (match_func && !(match_func)(lot, user_data))
4057  continue;
4058 
4059  /* Ok, this is a valid lot. Add it to our list of lots */
4060  retval = g_list_prepend (retval, lot);
4061  }
4062 
4063  if (sort_func)
4064  retval = g_list_sort (retval, sort_func);
4065 
4066  return retval;
4067 }
4068 
4069 gpointer
4070 xaccAccountForEachLot(const Account *acc,
4071  gpointer (*proc)(GNCLot *lot, void *data), void *data)
4072 {
4073  AccountPrivate *priv;
4074  LotList *node;
4075  gpointer result = NULL;
4076 
4077  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), NULL);
4078  g_return_val_if_fail(proc, NULL);
4079 
4080  priv = GET_PRIVATE(acc);
4081  for (node = priv->lots; node; node = node->next)
4082  if ((result = proc((GNCLot *)node->data, data)))
4083  break;
4084 
4085  return result;
4086 }
4087 
4088 static void
4089 set_boolean_key (Account *acc, std::vector<std::string> const & path, gboolean option)
4090 {
4091  GValue v = G_VALUE_INIT;
4092  g_return_if_fail(GNC_IS_ACCOUNT(acc));
4093 
4094  g_value_init (&v, G_TYPE_BOOLEAN);
4095  g_value_set_boolean (&v, option);
4096  xaccAccountBeginEdit (acc);
4097  qof_instance_set_path_kvp (QOF_INSTANCE (acc), &v, path);
4098  mark_account (acc);
4099  xaccAccountCommitEdit (acc);
4100  g_value_unset (&v);
4101 }
4102 
4103 static gboolean
4104 boolean_from_key (const Account *acc, std::vector<std::string> const & path)
4105 {
4106  GValue v = G_VALUE_INIT;
4107  gboolean retval = FALSE;
4108  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
4109  qof_instance_get_path_kvp (QOF_INSTANCE(acc), &v, path);
4110  if (G_VALUE_HOLDS_INT64 (&v))
4111  retval = (g_value_get_int64 (&v) != 0);
4112  if (G_VALUE_HOLDS_BOOLEAN (&v))
4113  retval = (g_value_get_boolean (&v));
4114  if (G_VALUE_HOLDS_STRING (&v))
4115  retval = !strcmp (g_value_get_string (&v), "true");
4116  g_value_unset (&v);
4117  return retval;
4118 }
4119 
4120 /********************************************************************\
4121 \********************************************************************/
4122 
4123 /* These functions use interchange gint64 and gboolean. Is that right? */
4124 gboolean
4126 {
4127  return boolean_from_key(acc, {"tax-related"});
4128 }
4129 
4130 void
4131 xaccAccountSetTaxRelated (Account *acc, gboolean tax_related)
4132 {
4133  set_boolean_key(acc, {"tax-related"}, tax_related);
4134 }
4135 
4136 const char *
4138 {
4139  auto priv = GET_PRIVATE (acc);
4140  if (priv->tax_us_code == is_unset)
4141  priv->tax_us_code = get_kvp_string_path (acc, {"tax-US", "code"});
4142  return priv->tax_us_code;
4143 }
4144 
4145 void
4146 xaccAccountSetTaxUSCode (Account *acc, const char *code)
4147 {
4148  auto priv = GET_PRIVATE (acc);
4149  if (priv->tax_us_code != is_unset)
4150  g_free (priv->tax_us_code);
4151  priv->tax_us_code = g_strdup (code);
4152  set_kvp_string_path (acc, {"tax-US", "code"}, priv->tax_us_code);
4153 }
4154 
4155 const char *
4157 {
4158  auto priv = GET_PRIVATE (acc);
4159  if (priv->tax_us_pns == is_unset)
4160  priv->tax_us_pns = get_kvp_string_path (acc, {"tax-US", "payer-name-source"});
4161  return priv->tax_us_pns;
4162  }
4163 
4164 void
4166 {
4167  auto priv = GET_PRIVATE (acc);
4168  if (priv->tax_us_pns != is_unset)
4169  g_free (priv->tax_us_pns);
4170  priv->tax_us_pns = g_strdup (source);
4171  set_kvp_string_path (acc, {"tax-US", "payer-name-source"}, priv->tax_us_pns);
4172 }
4173 
4174 gint64
4176 {
4177  gint64 copy_number = 0;
4178  GValue v = G_VALUE_INIT;
4179  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
4180  qof_instance_get_path_kvp (QOF_INSTANCE(acc), &v, {"tax-US", "copy-number"});
4181  if (G_VALUE_HOLDS_INT64 (&v))
4182  copy_number = g_value_get_int64 (&v);
4183 
4184  g_value_unset (&v);
4185  return (copy_number == 0) ? 1 : copy_number;
4186 }
4187 
4188 void
4189 xaccAccountSetTaxUSCopyNumber (Account *acc, gint64 copy_number)
4190 {
4191  g_return_if_fail(GNC_IS_ACCOUNT(acc));
4192  xaccAccountBeginEdit (acc);
4193  if (copy_number != 0)
4194  {
4195  GValue v = G_VALUE_INIT;
4196  g_value_init (&v, G_TYPE_INT64);
4197  g_value_set_int64 (&v, copy_number);
4198  qof_instance_set_path_kvp (QOF_INSTANCE (acc), &v, {"tax-US", "copy-number"});
4199  g_value_unset (&v);
4200  }
4201  else
4202  {
4203  qof_instance_set_path_kvp (QOF_INSTANCE (acc), nullptr, {"tax-US", "copy-number"});
4204  }
4205  mark_account (acc);
4206  xaccAccountCommitEdit (acc);
4207 }
4208 
4209 /*********************************************************************\
4210 \ ********************************************************************/
4211 
4212 
4214 {
4215  if (gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_ACCOUNTING_LABELS))
4216  return _(dflt_acct_debit_str);
4217 
4218  auto result = gnc_acct_debit_strs.find(acct_type);
4219  if (result != gnc_acct_debit_strs.end())
4220  return _(result->second);
4221  else
4222  return _(dflt_acct_debit_str);
4223 }
4224 
4226 {
4227  if (gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_ACCOUNTING_LABELS))
4228  return _(dflt_acct_credit_str);
4229 
4230  auto result = gnc_acct_credit_strs.find(acct_type);
4231  if (result != gnc_acct_credit_strs.end())
4232  return _(result->second);
4233  else
4234  return _(dflt_acct_credit_str);
4235 }
4236 
4237 /********************************************************************\
4238 \********************************************************************/
4239 
4240 gboolean
4242 {
4243  return boolean_from_key(acc, {"placeholder"});
4244 }
4245 
4246 void
4248 {
4249  set_boolean_key(acc, {"placeholder"}, val);
4250 }
4251 
4252 gboolean
4254 {
4255  return boolean_from_key(acc, {"import-append-text"});
4256 }
4257 
4258 void
4259 xaccAccountSetAppendText (Account *acc, gboolean val)
4260 {
4261  set_boolean_key(acc, {"import-append-text"}, val);
4262 }
4263 
4264 gboolean
4266 {
4267  if (GET_PRIVATE(acc)->type != ACCT_TYPE_EQUITY)
4268  return false;
4269  auto priv = GET_PRIVATE(acc);
4270  if (priv->equity_type == TriState::Unset)
4271  {
4272  auto equity_type = get_kvp_string_tag (acc, "equity-type");
4273  priv->equity_type = g_strcmp0 (equity_type, "opening-balance") ?
4274  TriState::False : TriState::True;
4275  g_free (equity_type);
4276  }
4277  return (priv->equity_type == TriState::True);
4278 }
4279 
4280 void
4282 {
4283  if (GET_PRIVATE(acc)->type != ACCT_TYPE_EQUITY)
4284  return;
4285  auto priv = GET_PRIVATE (acc);
4286  priv->equity_type = val ? TriState::True : TriState::False;
4287  set_kvp_string_tag(acc, "equity-type", val ? "opening-balance" : nullptr);
4288 }
4289 
4292 {
4293  GList *descendants, *node;
4294  GNCPlaceholderType ret = PLACEHOLDER_NONE;
4295 
4296  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), PLACEHOLDER_NONE);
4297  if (xaccAccountGetPlaceholder(acc)) return PLACEHOLDER_THIS;
4298 
4299  descendants = gnc_account_get_descendants(acc);
4300  for (node = descendants; node; node = node->next)
4301  if (xaccAccountGetPlaceholder((Account *) node->data))
4302  {
4303  ret = PLACEHOLDER_CHILD;
4304  break;
4305  }
4306 
4307  g_list_free(descendants);
4308  return ret;
4309 }
4310 
4311 /********************************************************************\
4312  \********************************************************************/
4313 
4314 gboolean
4316 {
4317  return boolean_from_key (acc, {KEY_RECONCILE_INFO, "auto-interest-transfer"});
4318 }
4319 
4320 void
4322 {
4323  set_boolean_key (acc, {KEY_RECONCILE_INFO, "auto-interest-transfer"}, val);
4324 }
4325 
4326 /********************************************************************\
4327 \********************************************************************/
4328 
4329 gboolean
4331 {
4332  return boolean_from_key (acc, {"hidden"});
4333 }
4334 
4335 void
4336 xaccAccountSetHidden (Account *acc, gboolean val)
4337 {
4338  set_boolean_key (acc, {"hidden"}, val);
4339 }
4340 
4341 gboolean
4343 {
4344  AccountPrivate *priv;
4345 
4346  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
4347 
4348  if (xaccAccountGetHidden(acc))
4349  return TRUE;
4350  priv = GET_PRIVATE(acc);
4351  while ((acc = priv->parent) != NULL)
4352  {
4353  priv = GET_PRIVATE(acc);
4354  if (xaccAccountGetHidden(acc))
4355  return TRUE;
4356  }
4357  return FALSE;
4358 }
4359 
4360 /********************************************************************\
4361 \********************************************************************/
4362 
4363 gboolean
4364 xaccAccountHasAncestor (const Account *acc, const Account * ancestor)
4365 {
4366  const Account *parent;
4367 
4368  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
4369  g_return_val_if_fail(GNC_IS_ACCOUNT(ancestor), FALSE);
4370 
4371  parent = acc;
4372  while (parent && parent != ancestor)
4373  parent = GET_PRIVATE(parent)->parent;
4374 
4375  return (parent == ancestor);
4376 }
4377 
4378 /********************************************************************\
4379 \********************************************************************/
4380 
4381 /* You must edit the functions in this block in tandem. KEEP THEM IN
4382  SYNC! */
4383 
4384 #define GNC_RETURN_ENUM_AS_STRING(x) case (ACCT_TYPE_ ## x): return #x;
4385 
4386 const char *
4388 {
4389  switch (type)
4390  {
4391  GNC_RETURN_ENUM_AS_STRING(NONE);
4392  GNC_RETURN_ENUM_AS_STRING(BANK);
4393  GNC_RETURN_ENUM_AS_STRING(CASH);
4394  GNC_RETURN_ENUM_AS_STRING(CREDIT);
4395  GNC_RETURN_ENUM_AS_STRING(ASSET);
4396  GNC_RETURN_ENUM_AS_STRING(LIABILITY);
4397  GNC_RETURN_ENUM_AS_STRING(STOCK);
4398  GNC_RETURN_ENUM_AS_STRING(MUTUAL);
4399  GNC_RETURN_ENUM_AS_STRING(CURRENCY);
4400  GNC_RETURN_ENUM_AS_STRING(INCOME);
4401  GNC_RETURN_ENUM_AS_STRING(EXPENSE);
4402  GNC_RETURN_ENUM_AS_STRING(EQUITY);
4403  GNC_RETURN_ENUM_AS_STRING(RECEIVABLE);
4404  GNC_RETURN_ENUM_AS_STRING(PAYABLE);
4405  GNC_RETURN_ENUM_AS_STRING(ROOT);
4406  GNC_RETURN_ENUM_AS_STRING(TRADING);
4407  GNC_RETURN_ENUM_AS_STRING(CHECKING);
4408  GNC_RETURN_ENUM_AS_STRING(SAVINGS);
4409  GNC_RETURN_ENUM_AS_STRING(MONEYMRKT);
4410  GNC_RETURN_ENUM_AS_STRING(CREDITLINE);
4411  default:
4412  PERR ("asked to translate unknown account type %d.\n", type);
4413  break;
4414  }
4415  return(NULL);
4416 }
4417 
4418 #undef GNC_RETURN_ENUM_AS_STRING
4419 
4420 #define GNC_RETURN_ON_MATCH(x) \
4421  if(g_strcmp0(#x, (str)) == 0) { *type = ACCT_TYPE_ ## x; return(TRUE); }
4422 
4423 gboolean
4425 {
4426 
4427  GNC_RETURN_ON_MATCH(NONE);
4428  GNC_RETURN_ON_MATCH(BANK);
4429  GNC_RETURN_ON_MATCH(CASH);
4430  GNC_RETURN_ON_MATCH(CREDIT);
4431  GNC_RETURN_ON_MATCH(ASSET);
4432  GNC_RETURN_ON_MATCH(LIABILITY);
4433  GNC_RETURN_ON_MATCH(STOCK);
4434  GNC_RETURN_ON_MATCH(MUTUAL);
4435  GNC_RETURN_ON_MATCH(CURRENCY);
4436  GNC_RETURN_ON_MATCH(INCOME);
4437  GNC_RETURN_ON_MATCH(EXPENSE);
4438  GNC_RETURN_ON_MATCH(EQUITY);
4439  GNC_RETURN_ON_MATCH(RECEIVABLE);
4440  GNC_RETURN_ON_MATCH(PAYABLE);
4441  GNC_RETURN_ON_MATCH(ROOT);
4442  GNC_RETURN_ON_MATCH(TRADING);
4443  GNC_RETURN_ON_MATCH(CHECKING);
4444  GNC_RETURN_ON_MATCH(SAVINGS);
4445  GNC_RETURN_ON_MATCH(MONEYMRKT);
4446  GNC_RETURN_ON_MATCH(CREDITLINE);
4447 
4448  PERR("asked to translate unknown account type string %s.\n",
4449  str ? str : "(null)");
4450 
4451  return(FALSE);
4452 }
4453 
4454 #undef GNC_RETURN_ON_MATCH
4455 
4456 /* impedance mismatch is a source of loss */
4458 xaccAccountStringToEnum(const char* str)
4459 {
4460  GNCAccountType type;
4461  gboolean rc;
4462  rc = xaccAccountStringToType(str, &type);
4463  if (FALSE == rc) return ACCT_TYPE_INVALID;
4464  return type;
4465 }
4466 
4467 /********************************************************************\
4468 \********************************************************************/
4469 
4470 static char const *
4471 account_type_name[NUM_ACCOUNT_TYPES] =
4472 {
4473  N_("Bank"),
4474  N_("Cash"),
4475  N_("Asset"),
4476  N_("Credit Card"),
4477  N_("Liability"),
4478  N_("Stock"),
4479  N_("Mutual Fund"),
4480  N_("Currency"),
4481  N_("Income"),
4482  N_("Expense"),
4483  N_("Equity"),
4484  N_("A/Receivable"),
4485  N_("A/Payable"),
4486  N_("Root"),
4487  N_("Trading")
4488  /*
4489  N_("Checking"),
4490  N_("Savings"),
4491  N_("Money Market"),
4492  N_("Credit Line")
4493  */
4494 };
4495 
4496 const char *
4498 {
4499  if (type < 0 || NUM_ACCOUNT_TYPES <= type ) return "";
4500  return _(account_type_name [type]);
4501 }
4502 
4503 /********************************************************************\
4504 \********************************************************************/
4505 
4506 guint32
4508 {
4509  switch (type)
4510  {
4511  case ACCT_TYPE_BANK:
4512  case ACCT_TYPE_CASH:
4513  case ACCT_TYPE_ASSET:
4514  case ACCT_TYPE_CREDIT:
4515  case ACCT_TYPE_LIABILITY:
4516  case ACCT_TYPE_INCOME:
4517  case ACCT_TYPE_EXPENSE:
4518  case ACCT_TYPE_EQUITY:
4519  return
4520  (1 << ACCT_TYPE_BANK) |
4521  (1 << ACCT_TYPE_CASH) |
4522  (1 << ACCT_TYPE_ASSET) |
4523  (1 << ACCT_TYPE_CREDIT) |
4524  (1 << ACCT_TYPE_LIABILITY) |
4525  (1 << ACCT_TYPE_INCOME) |
4526  (1 << ACCT_TYPE_EXPENSE) |
4527  (1 << ACCT_TYPE_EQUITY);
4528  case ACCT_TYPE_STOCK:
4529  case ACCT_TYPE_MUTUAL:
4530  case ACCT_TYPE_CURRENCY:
4531  return
4532  (1 << ACCT_TYPE_STOCK) |
4533  (1 << ACCT_TYPE_MUTUAL) |
4534  (1 << ACCT_TYPE_CURRENCY);
4535  case ACCT_TYPE_RECEIVABLE:
4536  return (1 << ACCT_TYPE_RECEIVABLE);
4537  case ACCT_TYPE_PAYABLE:
4538  return (1 << ACCT_TYPE_PAYABLE);
4539  case ACCT_TYPE_TRADING:
4540  return (1 << ACCT_TYPE_TRADING);
4541  default:
4542  PERR("bad account type: %d", type);
4543  return 0;
4544  }
4545 }
4546 guint32
4548 {
4549  switch (type)
4550  {
4551  case ACCT_TYPE_BANK:
4552  case ACCT_TYPE_CASH:
4553  case ACCT_TYPE_ASSET:
4554  case ACCT_TYPE_STOCK:
4555  case ACCT_TYPE_MUTUAL:
4556  case ACCT_TYPE_CURRENCY:
4557  case ACCT_TYPE_CREDIT:
4558  case ACCT_TYPE_LIABILITY:
4559  case ACCT_TYPE_RECEIVABLE:
4560  case ACCT_TYPE_PAYABLE:
4561  return
4562  (1 << ACCT_TYPE_BANK) |
4563  (1 << ACCT_TYPE_CASH) |
4564  (1 << ACCT_TYPE_ASSET) |
4565  (1 << ACCT_TYPE_STOCK) |
4566  (1 << ACCT_TYPE_MUTUAL) |
4567  (1 << ACCT_TYPE_CURRENCY) |
4568  (1 << ACCT_TYPE_CREDIT) |
4569  (1 << ACCT_TYPE_LIABILITY) |
4570  (1 << ACCT_TYPE_RECEIVABLE) |
4571  (1 << ACCT_TYPE_PAYABLE) |
4572  (1 << ACCT_TYPE_ROOT);
4573  case ACCT_TYPE_INCOME:
4574  case ACCT_TYPE_EXPENSE:
4575  return
4576  (1 << ACCT_TYPE_INCOME) |
4577  (1 << ACCT_TYPE_EXPENSE) |
4578  (1 << ACCT_TYPE_ROOT);
4579  case ACCT_TYPE_EQUITY:
4580  return
4581  (1 << ACCT_TYPE_EQUITY) |
4582  (1 << ACCT_TYPE_ROOT);
4583  case ACCT_TYPE_TRADING:
4584  return
4585  (1 << ACCT_TYPE_TRADING) |
4586  (1 << ACCT_TYPE_ROOT);
4587  default:
4588  PERR("bad account type: %d", type);
4589  return 0;
4590  }
4591 }
4592 
4593 gboolean
4595  GNCAccountType child_type)
4596 {
4597  /* ACCT_TYPE_NONE isn't compatible with anything, even ACCT_TYPE_NONE. */
4598  if (parent_type == ACCT_TYPE_NONE || child_type == ACCT_TYPE_NONE)
4599  return FALSE;
4600 
4601  /* ACCT_TYPE_ROOT can't have a parent account, and asking will raise
4602  * an error. */
4603  if (child_type == ACCT_TYPE_ROOT)
4604  return FALSE;
4605 
4606  return ((xaccParentAccountTypesCompatibleWith (child_type) &
4607  (1 << parent_type))
4608  != 0);
4609 }
4610 
4611 guint32
4613 {
4614  guint32 mask = (1 << NUM_ACCOUNT_TYPES) - 1;
4615  mask &= ~((1 << ACCT_TYPE_CURRENCY) | /* DEPRECATED */
4616  (1 << ACCT_TYPE_ROOT)); /* ROOT */
4617 
4618  return mask;
4619 }
4620 
4622 {
4623  switch (t)
4624  {
4625  case ACCT_TYPE_RECEIVABLE:
4626  case ACCT_TYPE_PAYABLE:
4627  return FALSE;
4628  default:
4631  }
4632 }
4633 
4636 {
4637  switch (t)
4638  {
4639  case ACCT_TYPE_BANK:
4640  case ACCT_TYPE_STOCK:
4641  case ACCT_TYPE_MONEYMRKT:
4642  case ACCT_TYPE_CHECKING:
4643  case ACCT_TYPE_SAVINGS:
4644  case ACCT_TYPE_MUTUAL:
4645  case ACCT_TYPE_CURRENCY:
4646  case ACCT_TYPE_CASH:
4647  case ACCT_TYPE_ASSET:
4648  case ACCT_TYPE_RECEIVABLE:
4649  return ACCT_TYPE_ASSET;
4650  case ACCT_TYPE_CREDIT:
4651  case ACCT_TYPE_LIABILITY:
4652  case ACCT_TYPE_PAYABLE:
4653  case ACCT_TYPE_CREDITLINE:
4654  return ACCT_TYPE_LIABILITY;
4655  case ACCT_TYPE_INCOME:
4656  return ACCT_TYPE_INCOME;
4657  case ACCT_TYPE_EXPENSE:
4658  return ACCT_TYPE_EXPENSE;
4659  case ACCT_TYPE_EQUITY:
4660  return ACCT_TYPE_EQUITY;
4661  case ACCT_TYPE_TRADING:
4662  default:
4663  return ACCT_TYPE_NONE;
4664  }
4665 }
4666 
4668 {
4669  switch (t)
4670  {
4671  case ACCT_TYPE_RECEIVABLE:
4672  case ACCT_TYPE_PAYABLE:
4673  return TRUE;
4674  default:
4675  return FALSE;
4676  }
4677 }
4678 
4680 {
4681  switch (t)
4682  {
4683  case ACCT_TYPE_EQUITY:
4684  return TRUE;
4685  default:
4686  return FALSE;
4687  }
4688 }
4689 
4690 gboolean
4692 {
4693  AccountPrivate *priv;
4694 
4695  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
4696 
4697  priv = GET_PRIVATE(acc);
4698  return (priv->type == ACCT_TYPE_STOCK || priv->type == ACCT_TYPE_MUTUAL ||
4699  priv->type == ACCT_TYPE_CURRENCY);
4700 }
4701 
4702 /********************************************************************\
4703 \********************************************************************/
4704 
4705 gboolean
4707 {
4708  gint64 date = 0;
4709  GValue v = G_VALUE_INIT;
4710  gboolean retval = FALSE;
4711  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
4712  qof_instance_get_path_kvp (QOF_INSTANCE(acc), &v, {KEY_RECONCILE_INFO, "last-date"});
4713  if (G_VALUE_HOLDS_INT64 (&v))
4714  date = g_value_get_int64 (&v);
4715 
4716  g_value_unset (&v);
4717  if (date)
4718  {
4719  if (last_date)
4720  *last_date = date;
4721  retval = TRUE;
4722  }
4723  g_value_unset (&v);
4724  return retval;
4725 }
4726 
4727 /********************************************************************\
4728 \********************************************************************/
4729 
4730 void
4732 {
4733  GValue v = G_VALUE_INIT;
4734  g_return_if_fail(GNC_IS_ACCOUNT(acc));
4735 
4736  g_value_init (&v, G_TYPE_INT64);
4737  g_value_set_int64 (&v, last_date);
4738  xaccAccountBeginEdit (acc);
4739  qof_instance_set_path_kvp (QOF_INSTANCE (acc), &v, {KEY_RECONCILE_INFO, "last-date"});
4740  mark_account (acc);
4741  xaccAccountCommitEdit (acc);
4742  g_value_unset (&v);
4743 }
4744 
4745 /********************************************************************\
4746 \********************************************************************/
4747 
4748 gboolean
4750  int *months, int *days)
4751 {
4752  GValue v1 = G_VALUE_INIT, v2 = G_VALUE_INIT;
4753  int64_t m = 0, d = 0;
4754  gboolean retval = FALSE;
4755 
4756  if (!acc) return FALSE;
4757  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
4758  qof_instance_get_path_kvp (QOF_INSTANCE(acc), &v1,
4759  {KEY_RECONCILE_INFO, "last-interval", "months"});
4760  qof_instance_get_path_kvp (QOF_INSTANCE(acc), &v2,
4761  {KEY_RECONCILE_INFO, "last-interval", "days"});
4762  if (G_VALUE_HOLDS_INT64 (&v1))
4763  m = g_value_get_int64 (&v1);
4764  if (G_VALUE_HOLDS_INT64 (&v2))
4765  d = g_value_get_int64 (&v2);
4766  if (m && d)
4767  {
4768  if (months)
4769  *months = m;
4770  if (days)
4771  *days = d;
4772  retval = TRUE;
4773  }
4774  g_value_unset (&v1);
4775  g_value_unset (&v2);
4776  return retval;
4777 }
4778 
4779 /********************************************************************\
4780 \********************************************************************/
4781 
4782 void
4783 xaccAccountSetReconcileLastInterval (Account *acc, int months, int days)
4784 {
4785  GValue v1 = G_VALUE_INIT, v2 = G_VALUE_INIT;
4786  g_return_if_fail(GNC_IS_ACCOUNT(acc));
4787 
4788  g_value_init (&v1, G_TYPE_INT64);
4789  g_value_set_int64 (&v1, months);
4790  g_value_init (&v2, G_TYPE_INT64);
4791  g_value_set_int64 (&v2, days);
4792  xaccAccountBeginEdit (acc);
4793  qof_instance_set_path_kvp (QOF_INSTANCE (acc), &v1,
4794  {KEY_RECONCILE_INFO, "last-interval", "months"});
4795  qof_instance_set_path_kvp (QOF_INSTANCE (acc), &v2,
4796  {KEY_RECONCILE_INFO, "last-interval", "days"});
4797  mark_account (acc);
4798  xaccAccountCommitEdit (acc);
4799  g_value_unset (&v1);
4800  g_value_unset (&v2);
4801 }
4802 
4803 /********************************************************************\
4804 \********************************************************************/
4805 
4806 gboolean
4808 {
4809  gint64 date = 0;
4810  gboolean retval = FALSE;
4811  GValue v = G_VALUE_INIT;
4812  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
4813  qof_instance_get_path_kvp (QOF_INSTANCE(acc), &v,
4814  {KEY_RECONCILE_INFO, KEY_POSTPONE, "date"});
4815  if (G_VALUE_HOLDS_INT64 (&v))
4816  date = g_value_get_int64 (&v);
4817 
4818  if (date)
4819  {
4820  if (postpone_date)
4821  *postpone_date = date;
4822  retval = TRUE;
4823  }
4824  g_value_unset (&v);
4825  return retval;
4826 }
4827 
4828 /********************************************************************\
4829 \********************************************************************/
4830 
4831 void
4833 {
4834  GValue v = G_VALUE_INIT;
4835  g_return_if_fail(GNC_IS_ACCOUNT(acc));
4836 
4837  g_value_init (&v, G_TYPE_INT64);
4838  g_value_set_int64 (&v, postpone_date);
4839  xaccAccountBeginEdit (acc);
4840  qof_instance_set_path_kvp (QOF_INSTANCE (acc), &v,
4841  {KEY_RECONCILE_INFO, KEY_POSTPONE, "date"});
4842  mark_account (acc);
4843  xaccAccountCommitEdit (acc);
4844  g_value_unset (&v);
4845 }
4846 
4847 /********************************************************************\
4848 \********************************************************************/
4849 
4850 gboolean
4852  gnc_numeric *balance)
4853 {
4854  gnc_numeric bal = gnc_numeric_zero ();
4855  GValue v = G_VALUE_INIT;
4856  gboolean retval = FALSE;
4857  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
4858  qof_instance_get_path_kvp (QOF_INSTANCE(acc), &v,
4859  {KEY_RECONCILE_INFO, KEY_POSTPONE, "balance"});
4860  if (G_VALUE_HOLDS_INT64 (&v))
4861  {
4862  bal = *(gnc_numeric*)g_value_get_boxed (&v);
4863  if (bal.denom)
4864  {
4865  if (balance)
4866  *balance = bal;
4867  retval = TRUE;
4868  }
4869  }
4870  g_value_unset (&v);
4871  return retval;
4872 }
4873 
4874 /********************************************************************\
4875 \********************************************************************/
4876 
4877 void
4879 {
4880  GValue v = G_VALUE_INIT;
4881  g_return_if_fail(GNC_IS_ACCOUNT(acc));
4882 
4883  g_value_init (&v, GNC_TYPE_NUMERIC);
4884  g_value_set_boxed (&v, &balance);
4885  xaccAccountBeginEdit (acc);
4886  qof_instance_set_path_kvp (QOF_INSTANCE (acc), &v,
4887  {KEY_RECONCILE_INFO, KEY_POSTPONE, "balance"});
4888  mark_account (acc);
4889  xaccAccountCommitEdit (acc);
4890  g_value_unset (&v);
4891 }
4892 
4893 /********************************************************************\
4894 
4895 \********************************************************************/
4896 
4897 void
4899 {
4900  if (!acc) return;
4901 
4902  xaccAccountBeginEdit (acc);
4903  qof_instance_set_path_kvp (QOF_INSTANCE(acc), nullptr, {KEY_RECONCILE_INFO, KEY_POSTPONE});
4904  mark_account (acc);
4905  xaccAccountCommitEdit (acc);
4906 }
4907 
4908 /********************************************************************\
4909 \********************************************************************/
4910 
4911 const char *
4913 {
4914  auto priv = GET_PRIVATE (acc);
4915  if (priv->last_num == is_unset)
4916  priv->last_num = get_kvp_string_tag (acc, "last-num");
4917  return priv->last_num;
4918 }
4919 
4920 /********************************************************************\
4921 \********************************************************************/
4922 
4923 void
4924 xaccAccountSetLastNum (Account *acc, const char *num)
4925 {
4926  auto priv = GET_PRIVATE (acc);
4927  if (priv->last_num != is_unset)
4928  g_free (priv->last_num);
4929  priv->last_num = g_strdup (num);
4930  set_kvp_string_tag (acc, "last-num", priv->last_num);
4931 }
4932 
4933 static Account *
4934 GetOrMakeOrphanAccount (Account *root, gnc_commodity * currency)
4935 {
4936  char * accname;
4937  Account * acc;
4938 
4939  g_return_val_if_fail (root, NULL);
4940 
4941  /* build the account name */
4942  if (!currency)
4943  {
4944  PERR ("No currency specified!");
4945  return NULL;
4946  }
4947 
4948  accname = g_strconcat (_("Orphaned Gains"), "-",
4949  gnc_commodity_get_mnemonic (currency), nullptr);
4950 
4951  /* See if we've got one of these going already ... */
4952  acc = gnc_account_lookup_by_name(root, accname);
4953 
4954  if (acc == NULL)
4955  {
4956  /* Guess not. We'll have to build one. */
4957  acc = xaccMallocAccount (gnc_account_get_book(root));
4958  xaccAccountBeginEdit (acc);
4959  xaccAccountSetName (acc, accname);
4960  xaccAccountSetCommodity (acc, currency);
4962  xaccAccountSetDescription (acc, _("Realized Gain/Loss"));
4963  xaccAccountSetNotes (acc,
4964  _("Realized Gains or Losses from "
4965  "Commodity or Trading Accounts "
4966  "that haven't been recorded elsewhere."));
4967 
4968  /* Hang the account off the root. */
4969  gnc_account_append_child (root, acc);
4970  xaccAccountCommitEdit (acc);
4971  }
4972 
4973  g_free (accname);
4974 
4975  return acc;
4976 }
4977 
4978 Account *
4979 xaccAccountGainsAccount (Account *acc, gnc_commodity *curr)
4980 {
4981  GValue v = G_VALUE_INIT;
4982  std::vector<std::string> path {KEY_LOT_MGMT, "gains-acct",
4984  GncGUID *guid = NULL;
4985  Account *gains_account;
4986 
4987  g_return_val_if_fail (acc != NULL, NULL);
4988  qof_instance_get_path_kvp (QOF_INSTANCE(acc), &v, path);
4989  if (G_VALUE_HOLDS_BOXED (&v))
4990  guid = (GncGUID*)g_value_get_boxed (&v);
4991  if (guid == NULL) /* No gains account for this currency */
4992  {
4993  gains_account = GetOrMakeOrphanAccount (gnc_account_get_root (acc),
4994  curr);
4995  guid = (GncGUID*)qof_instance_get_guid (QOF_INSTANCE (gains_account));
4996  xaccAccountBeginEdit (acc);
4997  {
4998  GValue vr = G_VALUE_INIT;
4999  g_value_init (&vr, GNC_TYPE_GUID);
5000  g_value_set_boxed (&vr, guid);
5001  qof_instance_set_path_kvp (QOF_INSTANCE (acc), &vr, path);
5002  qof_instance_set_dirty (QOF_INSTANCE (acc));
5003  g_value_unset (&vr);
5004  }
5005  xaccAccountCommitEdit (acc);
5006  }
5007  else
5008  gains_account = xaccAccountLookup (guid,
5009  qof_instance_get_book(acc));
5010 
5011  g_value_unset (&v);
5012  return gains_account;
5013 }
5014 
5015 /********************************************************************\
5016 \********************************************************************/
5017 
5018 void
5019 dxaccAccountSetPriceSrc(Account *acc, const char *src)
5020 {
5021  if (!acc) return;
5022 
5023  if (xaccAccountIsPriced(acc))
5024  set_kvp_string_tag (acc, "old-price-source", src);
5025 }
5026 
5027 /********************************************************************\
5028 \********************************************************************/
5029 
5030 const char*
5032 {
5033  static char *source = nullptr;
5034  if (!acc) return NULL;
5035 
5036  if (!xaccAccountIsPriced(acc)) return NULL;
5037 
5038  g_free (source);
5039  source = get_kvp_string_tag (acc, "old-price-source");
5040  return source;
5041 }
5042 
5043 /********************************************************************\
5044 \********************************************************************/
5045 
5046 void
5047 dxaccAccountSetQuoteTZ(Account *acc, const char *tz)
5048 {
5049  if (!acc) return;
5050  if (!xaccAccountIsPriced(acc)) return;
5051  set_kvp_string_tag (acc, "old-quote-tz", tz);
5052 }
5053 
5054 /********************************************************************\
5055 \********************************************************************/
5056 
5057 const char*
5059 {
5060  static char *quote_tz = nullptr;
5061  if (!acc) return NULL;
5062  if (!xaccAccountIsPriced(acc)) return NULL;
5063  g_free (quote_tz);
5064  quote_tz = get_kvp_string_tag (acc, "old-quote-tz");
5065  return quote_tz;
5066 }
5067 
5068 /********************************************************************\
5069 \********************************************************************/
5070 
5071 void
5073 {
5074  GValue v = G_VALUE_INIT;
5075  if (!acc) return;
5076 
5077  xaccAccountBeginEdit (acc);
5078  /* Would have been nice to use G_TYPE_BOOLEAN, but the other
5079  * boolean kvps save the value as "true" or "false" and that would
5080  * be file-incompatible with this.
5081  */
5082  g_value_init (&v, G_TYPE_INT64);
5083  g_value_set_int64 (&v, status);
5084  qof_instance_set_path_kvp (QOF_INSTANCE (acc), &v,
5085  {KEY_RECONCILE_INFO, KEY_INCLUDE_CHILDREN});
5086  mark_account(acc);
5087  xaccAccountCommitEdit (acc);
5088  g_value_unset (&v);
5089 }
5090 
5091 /********************************************************************\
5092 \********************************************************************/
5093 
5094 gboolean
5096 {
5097  /* access the account's kvp-data for status and return that, if no value
5098  * is found then we can assume not to include the children, that being
5099  * the default behaviour
5100  */
5101  GValue v = G_VALUE_INIT;
5102  gboolean retval;
5103  if (!acc) return FALSE;
5104  qof_instance_get_path_kvp (QOF_INSTANCE (acc), &v,
5105  {KEY_RECONCILE_INFO, KEY_INCLUDE_CHILDREN});
5106  retval = G_VALUE_HOLDS_INT64 (&v) ? g_value_get_int64 (&v) : FALSE;
5107  g_value_unset (&v);
5108  return retval;
5109 }
5110 
5111 /********************************************************************\
5112 \********************************************************************/
5113 
5114 /* The caller of this function can get back one or both of the
5115  * matching split and transaction pointers, depending on whether
5116  * a valid pointer to the location to store those pointers is
5117  * passed.
5118  */
5119 static void
5120 finder_help_function(const Account *acc, const char *description,
5121  Split **split, Transaction **trans )
5122 {
5123  AccountPrivate *priv;
5124  GList *slp;
5125 
5126  /* First, make sure we set the data to NULL BEFORE we start */
5127  if (split) *split = NULL;
5128  if (trans) *trans = NULL;
5129 
5130  /* Then see if we have any work to do */
5131  if (acc == NULL) return;
5132 
5133  /* Why is this loop iterated backwards ?? Presumably because the split
5134  * list is in date order, and the most recent matches should be
5135  * returned!? */
5136  priv = GET_PRIVATE(acc);
5137  for (slp = g_list_last(priv->splits); slp; slp = slp->prev)
5138  {
5139  Split *lsplit = static_cast<Split*>(slp->data);
5140  Transaction *ltrans = xaccSplitGetParent(lsplit);
5141 
5142  if (g_strcmp0 (description, xaccTransGetDescription (ltrans)) == 0)
5143  {
5144  if (split) *split = lsplit;
5145  if (trans) *trans = ltrans;
5146  return;
5147  }
5148  }
5149 }
5150 
5151 Split *
5152 xaccAccountFindSplitByDesc(const Account *acc, const char *description)
5153 {
5154  Split *split;
5155 
5156  /* Get the split which has a transaction matching the description. */
5157  finder_help_function(acc, description, &split, NULL);
5158  return split;
5159 }
5160 
5161 /* This routine is for finding a matching transaction in an account by
5162  * matching on the description field. [CAS: The rest of this comment
5163  * seems to belong somewhere else.] This routine is used for
5164  * auto-filling in registers with a default leading account. The
5165  * dest_trans is a transaction used for currency checking. */
5166 Transaction *
5167 xaccAccountFindTransByDesc(const Account *acc, const char *description)
5168 {
5169  Transaction *trans;
5170 
5171  /* Get the translation matching the description. */
5172  finder_help_function(acc, description, NULL, &trans);
5173  return trans;
5174 }
5175 
5176 /* ================================================================ */
5177 /* Concatenation, Merging functions */
5178 
5179 void
5180 gnc_account_join_children (Account *to_parent, Account *from_parent)
5181 {
5182  AccountPrivate *from_priv;
5183  GList *children, *node;
5184 
5185  /* errors */
5186  g_return_if_fail(GNC_IS_ACCOUNT(to_parent));
5187  g_return_if_fail(GNC_IS_ACCOUNT(from_parent));
5188 
5189  /* optimizations */
5190  from_priv = GET_PRIVATE(from_parent);
5191  if (!from_priv->children)
5192  return;
5193 
5194  ENTER (" ");
5195  children = g_list_copy(from_priv->children);
5196  for (node = children; node; node = g_list_next(node))
5197  gnc_account_append_child(to_parent, static_cast <Account*> (node->data));
5198  g_list_free(children);
5199  LEAVE (" ");
5200 }
5201 /********************************************************************\
5202 \********************************************************************/
5203 
5204 void
5206 {
5207  AccountPrivate *ppriv, *priv_a, *priv_b;
5208  GList *node_a, *node_b, *work, *worker;
5209 
5210  g_return_if_fail(GNC_IS_ACCOUNT(parent));
5211 
5212  ppriv = GET_PRIVATE(parent);
5213  for (node_a = ppriv->children; node_a; node_a = node_a->next)
5214  {
5215  Account *acc_a = static_cast <Account*> (node_a->data);
5216 
5217  priv_a = GET_PRIVATE(acc_a);
5218  for (node_b = node_a->next; node_b; node_b = g_list_next(node_b))
5219  {
5220  Account *acc_b = static_cast <Account*> (node_b->data);
5221 
5222  priv_b = GET_PRIVATE(acc_b);
5223  if (0 != null_strcmp(priv_a->accountName, priv_b->accountName))
5224  continue;
5225  if (0 != null_strcmp(priv_a->accountCode, priv_b->accountCode))
5226  continue;
5227  if (0 != null_strcmp(priv_a->description, priv_b->description))
5228  continue;
5229  if (0 != null_strcmp(xaccAccountGetColor(acc_a),
5230  xaccAccountGetColor(acc_b)))
5231  continue;
5232  if (!gnc_commodity_equiv(priv_a->commodity, priv_b->commodity))
5233  continue;
5234  if (0 != null_strcmp(xaccAccountGetNotes(acc_a),
5235  xaccAccountGetNotes(acc_b)))
5236  continue;
5237  if (priv_a->type != priv_b->type)
5238  continue;
5239 
5240  /* consolidate children */
5241  if (priv_b->children)
5242  {
5243  work = g_list_copy(priv_b->children);
5244  for (worker = work; worker; worker = g_list_next(worker))
5245  gnc_account_append_child (acc_a, (Account *)worker->data);
5246  g_list_free(work);
5247 
5248  qof_event_gen (&acc_a->inst, QOF_EVENT_MODIFY, NULL);
5249  qof_event_gen (&acc_b->inst, QOF_EVENT_MODIFY, NULL);
5250  }
5251 
5252  /* recurse to do the children's children */
5254 
5255  /* consolidate transactions */
5256  while (priv_b->splits)
5257  xaccSplitSetAccount (static_cast <Split*> (priv_b->splits->data), acc_a);
5258 
5259  /* move back one before removal. next iteration around the loop
5260  * will get the node after node_b */
5261  node_b = g_list_previous(node_b);
5262 
5263  /* The destroy function will remove from list -- node_a is ok,
5264  * it's before node_b */
5265  xaccAccountBeginEdit (acc_b);
5266  xaccAccountDestroy (acc_b);
5267  }
5268  }
5269 }
5270 
5271 /* ================================================================ */
5272 /* Transaction Traversal functions */
5273 
5274 
5275 void
5277 {
5278  GList *lp;
5279 
5280  for (lp = splits; lp; lp = lp->next)
5281  {
5282  Split *s = static_cast <Split*> (lp->data);
5283  Transaction *trans = s->parent;
5284 
5285  if (trans)
5286  trans->marker = 0;
5287  }
5288 }
5289 
5290 /* original function */
5291 void
5293 {
5294  AccountPrivate *priv;
5295 
5296  if (!account)
5297  return;
5298  priv = GET_PRIVATE(account);
5300 }
5301 
5302 gboolean
5303 xaccTransactionTraverse (Transaction *trans, int stage)
5304 {
5305  if (trans == NULL) return FALSE;
5306 
5307  if (trans->marker < stage)
5308  {
5309  trans->marker = stage;
5310  return TRUE;
5311  }
5312 
5313  return FALSE;
5314 }
5315 
5316 static void do_one_split (Split *s, gpointer data)
5317 {
5318  Transaction *trans = s->parent;
5319  trans->marker = 0;
5320 }
5321 
5322 static void do_one_account (Account *account, gpointer data)
5323 {
5324  AccountPrivate *priv = GET_PRIVATE(account);
5325  g_list_foreach(priv->splits, (GFunc)do_one_split, NULL);
5326 }
5327 
5328 /* Replacement for xaccGroupBeginStagedTransactionTraversals */
5329 void
5331 {
5332  GList *descendants;
5333 
5334  descendants = gnc_account_get_descendants(account);
5335  g_list_foreach(descendants, (GFunc)do_one_account, NULL);
5336  g_list_free(descendants);
5337 }
5338 
5339 int
5341  unsigned int stage,
5342  TransactionCallback thunk,
5343  void *cb_data)
5344 {
5345  AccountPrivate *priv;
5346  GList *split_p;
5347  GList *next;
5348  Transaction *trans;
5349  Split *s;
5350  int retval;
5351 
5352  if (!acc) return 0;
5353 
5354  priv = GET_PRIVATE(acc);
5355  for (split_p = priv->splits; split_p; split_p = next)
5356  {
5357  /* Get the next element in the split list now, just in case some
5358  * naughty thunk destroys the one we're using. This reduces, but
5359  * does not eliminate, the possibility of undefined results if
5360  * a thunk removes splits from this account. */
5361  next = g_list_next(split_p);
5362 
5363  s = static_cast <Split*> (split_p->data);
5364  trans = s->parent;
5365  if (trans && (trans->marker < stage))
5366  {
5367  trans->marker = stage;
5368  if (thunk)
5369  {
5370  retval = thunk(trans, cb_data);
5371  if (retval) return retval;
5372  }
5373  }
5374  }
5375 
5376  return 0;
5377 }
5378 
5379 int
5381  unsigned int stage,
5382  TransactionCallback thunk,
5383  void *cb_data)
5384 {
5385  const AccountPrivate *priv;
5386  GList *acc_p, *split_p;
5387  Transaction *trans;
5388  Split *s;
5389  int retval;
5390 
5391  if (!acc) return 0;
5392 
5393  /* depth first traversal */
5394  priv = GET_PRIVATE(acc);
5395  for (acc_p = priv->children; acc_p; acc_p = g_list_next(acc_p))
5396  {
5397  retval = gnc_account_tree_staged_transaction_traversal(static_cast <Account*> (acc_p->data),
5398  stage, thunk, cb_data);
5399  if (retval) return retval;
5400  }
5401 
5402  /* Now this account */
5403  for (split_p = priv->splits; split_p; split_p = g_list_next(split_p))
5404  {
5405  s = static_cast <Split*> (split_p->data);
5406  trans = s->parent;
5407  if (trans && (trans->marker < stage))
5408  {
5409  trans->marker = stage;
5410  if (thunk)
5411  {
5412  retval = thunk(trans, cb_data);
5413  if (retval) return retval;
5414  }
5415  }
5416  }
5417 
5418  return 0;
5419 }
5420 
5421 /********************************************************************\
5422 \********************************************************************/
5423 
5424 int
5426  int (*proc)(Transaction *t, void *data),
5427  void *data)
5428 {
5429  if (!acc || !proc) return 0;
5430 
5432  return gnc_account_tree_staged_transaction_traversal (acc, 42, proc, data);
5433 }
5434 
5435 
5436 gint
5437 xaccAccountForEachTransaction(const Account *acc, TransactionCallback proc,
5438  void *data)
5439 {
5440  if (!acc || !proc) return 0;
5442  return xaccAccountStagedTransactionTraversal(acc, 42, proc, data);
5443 }
5444 
5445 /* ================================================================ */
5446 /* The following functions are used by
5447  * src/import-export/import-backend.c to manipulate the contra-account
5448  * matching data. See src/import-export/import-backend.c for explanations.
5449  */
5450 
5451 #define IMAP_FRAME "import-map"
5452 #define IMAP_FRAME_BAYES "import-map-bayes"
5453 
5454 /* Obtain an ImportMatchMap object from an Account or a Book */
5457 {
5458  GncImportMatchMap *imap;
5459 
5460  if (!acc) return NULL;
5461 
5462  imap = g_new0(GncImportMatchMap, 1);
5463 
5464  /* Cache the book for easy lookups; store the account/book for
5465  * marking dirtiness
5466  */
5467  imap->acc = acc;
5468  imap->book = gnc_account_get_book (acc);
5469 
5470  return imap;
5471 }
5472 
5473 /* Look up an Account in the map */
5474 Account*
5475 gnc_account_imap_find_account (GncImportMatchMap *imap,
5476  const char *category,
5477  const char *key)
5478 {
5479  GValue v = G_VALUE_INIT;
5480  GncGUID * guid = NULL;
5481  Account *retval;
5482  if (!imap || !key) return NULL;
5483  std::vector<std::string> path {IMAP_FRAME};
5484  if (category)
5485  path.push_back (category);
5486  path.push_back (key);
5487  qof_instance_get_path_kvp (QOF_INSTANCE (imap->acc), &v, path);
5488  if (G_VALUE_HOLDS_BOXED (&v))
5489  guid = (GncGUID*)g_value_get_boxed (&v);
5490  retval = xaccAccountLookup (guid, imap->book);
5491  g_value_unset (&v);
5492  return retval;
5493 }
5494 
5495 /* Store an Account in the map */
5496 void
5497 gnc_account_imap_add_account (GncImportMatchMap *imap,
5498  const char *category,
5499  const char *key,
5500  Account *acc)
5501 {
5502  GValue v = G_VALUE_INIT;
5503  if (!imap || !key || !acc || (strlen (key) == 0)) return;
5504  std::vector<std::string> path {IMAP_FRAME};
5505  if (category)
5506  path.emplace_back (category);
5507  path.emplace_back (key);
5508  g_value_init (&v, GNC_TYPE_GUID);
5509  g_value_set_boxed (&v, xaccAccountGetGUID (acc));
5510  xaccAccountBeginEdit (imap->acc);
5511  qof_instance_set_path_kvp (QOF_INSTANCE (imap->acc), &v, path);
5512  qof_instance_set_dirty (QOF_INSTANCE (imap->acc));
5513  xaccAccountCommitEdit (imap->acc);
5514  g_value_unset (&v);
5515 }
5516 
5517 /* Remove a reference to an Account in the map */
5518 void
5519 gnc_account_imap_delete_account (GncImportMatchMap *imap,
5520  const char *category,
5521  const char *key)
5522 {
5523  if (!imap || !key) return;
5524  std::vector<std::string> path {IMAP_FRAME};
5525  if (category)
5526  path.emplace_back (category);
5527  path.emplace_back (key);
5528  xaccAccountBeginEdit (imap->acc);
5529  if (qof_instance_has_path_slot (QOF_INSTANCE (imap->acc), path))
5530  {
5531  qof_instance_slot_path_delete (QOF_INSTANCE (imap->acc), path);
5532  if (category)
5533  qof_instance_slot_path_delete_if_empty (QOF_INSTANCE (imap->acc), {IMAP_FRAME, category});
5534  qof_instance_slot_path_delete_if_empty (QOF_INSTANCE (imap->acc), {IMAP_FRAME});
5535  }
5536  qof_instance_set_dirty (QOF_INSTANCE (imap->acc));
5537  xaccAccountCommitEdit (imap->acc);
5538 }
5539 
5540 /*--------------------------------------------------------------------------
5541  Below here is the bayes transaction to account matching system
5542 --------------------------------------------------------------------------*/
5543 
5544 
5550 {
5551  double product; /* product of probabilities */
5552  double product_difference; /* product of (1-probabilities) */
5553 };
5554 
5556 {
5557  std::string account_guid;
5558  int64_t token_count;
5559 };
5560 
5565 {
5566  std::vector<AccountTokenCount> accounts;
5567  int64_t total_count;
5568 };
5569 
5574 {
5575  std::string account_guid;
5576  int32_t probability;
5577 };
5578 
5579 static void
5580 build_token_info(char const * suffix, KvpValue * value, TokenAccountsInfo & tokenInfo)
5581 {
5582  if (strlen(suffix) == GUID_ENCODING_LENGTH)
5583  {
5584  tokenInfo.total_count += value->get<int64_t>();
5585  /*By convention, the key ends with the account GUID.*/
5586  tokenInfo.accounts.emplace_back(AccountTokenCount{std::string{suffix}, value->get<int64_t>()});
5587  }
5588 }
5589 
5593 static constexpr int probability_factor = 100000;
5594 
5595 static FinalProbabilityVec
5596 build_probabilities(ProbabilityVec const & first_pass)
5597 {
5598  FinalProbabilityVec ret;
5599  for (auto const & first_pass_prob : first_pass)
5600  {
5601  auto const & account_probability = first_pass_prob.second;
5602  /* P(AB) = A*B / [A*B + (1-A)*(1-B)]
5603  * NOTE: so we only keep track of a running product(A*B*C...)
5604  * and product difference ((1-A)(1-B)...)
5605  */
5606  int32_t probability = (account_probability.product /
5607  (account_probability.product + account_probability.product_difference)) * probability_factor;
5608  ret.push_back({first_pass_prob.first, probability});
5609  }
5610  return ret;
5611 }
5612 
5613 static AccountInfo
5614 highest_probability(FinalProbabilityVec const & probabilities)
5615 {
5616  AccountInfo ret {"", std::numeric_limits<int32_t>::min()};
5617  for (auto const & prob : probabilities)
5618  if (prob.second > ret.probability)
5619  ret = AccountInfo {prob.first, prob.second};
5620  return ret;
5621 }
5622 
5623 static ProbabilityVec
5624 get_first_pass_probabilities(GncImportMatchMap * imap, GList * tokens)
5625 {
5626  ProbabilityVec ret;
5627  /* find the probability for each account that contains any of the tokens
5628  * in the input tokens list. */
5629  for (auto current_token = tokens; current_token; current_token = current_token->next)
5630  {
5631  TokenAccountsInfo tokenInfo{};
5632  auto path = std::string{IMAP_FRAME_BAYES "/"} + static_cast <char const *> (current_token->data) + "/";
5633  qof_instance_foreach_slot_prefix (QOF_INSTANCE (imap->acc), path, &build_token_info, tokenInfo);
5634  for (auto const & current_account_token : tokenInfo.accounts)
5635  {
5636  auto item = std::find_if(ret.begin(), ret.end(), [&current_account_token]
5637  (std::pair<std::string, AccountProbability> const & a) {
5638  return current_account_token.account_guid == a.first;
5639  });
5640  if (item != ret.end())
5641  {/* This account is already in the map */
5642  item->second.product = ((double)current_account_token.token_count /
5643  (double)tokenInfo.total_count) * item->second.product;
5644  item->second.product_difference = ((double)1 - ((double)current_account_token.token_count /
5645  (double)tokenInfo.total_count)) * item->second.product_difference;
5646  }
5647  else
5648  {
5649  /* add a new entry */
5650  AccountProbability new_probability;
5651  new_probability.product = ((double)current_account_token.token_count /
5652  (double)tokenInfo.total_count);
5653  new_probability.product_difference = 1 - (new_probability.product);
5654  ret.push_back({current_account_token.account_guid, std::move(new_probability)});
5655  }
5656  } /* for all accounts in tokenInfo */
5657  }
5658  return ret;
5659 }
5660 
5661 static std::string
5662 look_for_old_separator_descendants (Account *root, std::string const & full_name, const gchar *separator)
5663 {
5664  GList *top_accounts, *ptr;
5665  gint found_len = 0;
5666  gchar found_sep;
5667  top_accounts = gnc_account_get_descendants (root);
5668  PINFO("Incoming full_name is '%s', current separator is '%s'", full_name.c_str (), separator);
5669  /* Go through list of top level accounts */
5670  for (ptr = top_accounts; ptr; ptr = g_list_next (ptr))
5671  {
5672  const gchar *name = xaccAccountGetName (static_cast <Account const *> (ptr->data));
5673  // we are looking for the longest top level account that matches
5674  if (g_str_has_prefix (full_name.c_str (), name))
5675  {
5676  gint name_len = strlen (name);
5677  const gchar old_sep = full_name[name_len];
5678  if (!g_ascii_isalnum (old_sep)) // test for non alpha numeric
5679  {
5680  if (name_len > found_len)
5681  {
5682  found_sep = full_name[name_len];
5683  found_len = name_len;
5684  }
5685  }
5686  }
5687  }
5688  g_list_free (top_accounts); // Free the List
5689  std::string new_name {full_name};
5690  if (found_len > 1)
5691  std::replace (new_name.begin (), new_name.end (), found_sep, *separator);
5692  PINFO ("Return full_name is '%s'", new_name.c_str ());
5693  return new_name;
5694 }
5695 
5696 static std::string
5697 get_guid_from_account_name (Account * root, std::string const & name)
5698 {
5699  auto map_account = gnc_account_lookup_by_full_name (root, name.c_str ());
5700  if (!map_account)
5701  {
5702  auto temp_account_name = look_for_old_separator_descendants (root, name,
5704  map_account = gnc_account_lookup_by_full_name (root, temp_account_name.c_str ());
5705  }
5706  auto temp_guid = gnc::GUID {*xaccAccountGetGUID (map_account)};
5707  return temp_guid.to_string ();
5708 }
5709 
5710 static FlatKvpEntry
5711 convert_entry (KvpEntry entry, Account* root)
5712 {
5713  /*We need to make a copy here.*/
5714  auto account_name = entry.first.back();
5715  if (!gnc::GUID::is_valid_guid (account_name))
5716  {
5717  /* Earlier version stored the account name in the import map, and
5718  * there were early beta versions of 2.7 that stored a GUID.
5719  * If there is no GUID, we assume it's an account name. */
5720  /* Take off the account name and replace it with the GUID */
5721  entry.first.pop_back();
5722  auto guid_str = get_guid_from_account_name (root, account_name);
5723  entry.first.emplace_back (guid_str);
5724  }
5725  std::string new_key {std::accumulate (entry.first.begin(), entry.first.end(), std::string {})};
5726  new_key = IMAP_FRAME_BAYES + new_key;
5727  return {new_key, entry.second};
5728 }
5729 
5730 static std::vector<FlatKvpEntry>
5731 get_flat_imap (Account * acc)
5732 {
5733  auto frame = qof_instance_get_slots (QOF_INSTANCE (acc));
5734  auto slot = frame->get_slot ({IMAP_FRAME_BAYES});
5735  if (!slot)
5736  return {};
5737  auto imap_frame = slot->get<KvpFrame*> ();
5738  auto flat_kvp = imap_frame->flatten_kvp ();
5739  auto root = gnc_account_get_root (acc);
5740  std::vector <FlatKvpEntry> ret;
5741  for (auto const & flat_entry : flat_kvp)
5742  {
5743  auto converted_entry = convert_entry (flat_entry, root);
5744  /*If the entry was invalid, we don't perpetuate it.*/
5745  if (converted_entry.first.size())
5746  ret.emplace_back (converted_entry);
5747  }
5748  return ret;
5749 }
5750 
5751 static bool
5752 convert_imap_account_bayes_to_flat (Account *acc)
5753 {
5754  auto frame = qof_instance_get_slots (QOF_INSTANCE (acc));
5755  if (!frame->get_keys().size())
5756  return false;
5757  auto flat_imap = get_flat_imap(acc);
5758  if (!flat_imap.size ())
5759  return false;
5760  xaccAccountBeginEdit(acc);
5761  frame->set({IMAP_FRAME_BAYES}, nullptr);
5762  std::for_each(flat_imap.begin(), flat_imap.end(),
5763  [&frame] (FlatKvpEntry const & entry) {
5764  frame->set({entry.first.c_str()}, entry.second);
5765  });
5766  qof_instance_set_dirty (QOF_INSTANCE (acc));
5767  xaccAccountCommitEdit(acc);
5768  return true;
5769 }
5770 
5771 /*
5772  * Checks for import map data and converts them when found.
5773  */
5774 static bool
5775 imap_convert_bayes_to_flat (QofBook * book)
5776 {
5777  auto root = gnc_book_get_root_account (book);
5778  auto accts = gnc_account_get_descendants_sorted (root);
5779  bool ret = false;
5780  for (auto ptr = accts; ptr; ptr = g_list_next (ptr))
5781  {
5782  Account *acc = static_cast <Account*> (ptr->data);
5783  if (convert_imap_account_bayes_to_flat (acc))
5784  {
5785  ret = true;
5786  gnc_features_set_used (book, GNC_FEATURE_GUID_FLAT_BAYESIAN);
5787  }
5788  }
5789  g_list_free (accts);
5790  return ret;
5791 }
5792 
5793 void
5795 {
5796  imap_convert_bayes_to_flat_run = false;
5797 }
5798 
5799 /*
5800  * Here we check to see the state of import map data.
5801  *
5802  * If the GUID_FLAT_BAYESIAN feature flag is set, everything
5803  * should be fine.
5804  *
5805  * If it is not set, there are two possibilities: import data
5806  * are present from a previous version or not. If they are,
5807  * they are converted, and the feature flag set. If there are
5808  * no previous data, nothing is done.
5809  */
5810 static void
5811 check_import_map_data (QofBook *book)
5812 {
5813  if (gnc_features_check_used (book, GNC_FEATURE_GUID_FLAT_BAYESIAN) ||
5814  imap_convert_bayes_to_flat_run)
5815  return;
5816 
5817  /* This function will set GNC_FEATURE_GUID_FLAT_BAYESIAN if necessary.*/
5818  imap_convert_bayes_to_flat (book);
5819  imap_convert_bayes_to_flat_run = true;
5820 }
5821 
5822 static constexpr double threshold = .90 * probability_factor; /* 90% */
5823 
5825 Account*
5827 {
5828  if (!imap)
5829  return nullptr;
5830  check_import_map_data (imap->book);
5831  auto first_pass = get_first_pass_probabilities(imap, tokens);
5832  if (!first_pass.size())
5833  return nullptr;
5834  auto final_probabilities = build_probabilities(first_pass);
5835  if (!final_probabilities.size())
5836  return nullptr;
5837  auto best = highest_probability(final_probabilities);
5838  if (best.account_guid == "")
5839  return nullptr;
5840  if (best.probability < threshold)
5841  return nullptr;
5842  gnc::GUID guid;
5843  try {
5844  guid = gnc::GUID::from_string(best.account_guid);
5845  } catch (gnc::guid_syntax_exception&) {
5846  return nullptr;
5847  }
5848  auto account = xaccAccountLookup (reinterpret_cast<GncGUID*>(&guid), imap->book);
5849  return account;
5850 }
5851 
5852 static void
5853 change_imap_entry (GncImportMatchMap *imap, std::string const & path, int64_t token_count)
5854 {
5855  GValue value = G_VALUE_INIT;
5856 
5857  PINFO("Source Account is '%s', Count is '%" G_GINT64_FORMAT "'",
5858  xaccAccountGetName (imap->acc), token_count);
5859 
5860  // check for existing guid entry
5861  if (qof_instance_has_slot (QOF_INSTANCE(imap->acc), path.c_str ()))
5862  {
5863  int64_t existing_token_count = 0;
5864 
5865  // get the existing_token_count value
5866  qof_instance_get_path_kvp (QOF_INSTANCE (imap->acc), &value, {path});
5867 
5868  if (G_VALUE_HOLDS_INT64 (&value))
5869  existing_token_count = g_value_get_int64 (&value);
5870 
5871  PINFO("found existing value of '%" G_GINT64_FORMAT "'", existing_token_count);
5872 
5873  token_count = token_count + existing_token_count;
5874  }
5875 
5876  if (!G_IS_VALUE (&value))
5877  g_value_init (&value, G_TYPE_INT64);
5878 
5879  g_value_set_int64 (&value, token_count);
5880 
5881  // Add or Update the entry based on guid
5882  qof_instance_set_path_kvp (QOF_INSTANCE (imap->acc), &value, {path});
5883  gnc_features_set_used (imap->book, GNC_FEATURE_GUID_FLAT_BAYESIAN);
5884  g_value_unset (&value);
5885 }
5886 
5888 void
5890  GList *tokens,
5891  Account *acc)
5892 {
5893  GList *current_token;
5894  gint64 token_count;
5895  char *account_fullname;
5896  char *guid_string;
5897 
5898  ENTER(" ");
5899  if (!imap)
5900  {
5901  LEAVE(" ");
5902  return;
5903  }
5904  check_import_map_data (imap->book);
5905 
5906  g_return_if_fail (acc != NULL);
5907  account_fullname = gnc_account_get_full_name(acc);
5908  xaccAccountBeginEdit (imap->acc);
5909 
5910  PINFO("account name: '%s'", account_fullname);
5911 
5912  guid_string = guid_to_string (xaccAccountGetGUID (acc));
5913 
5914  /* process each token in the list */
5915  for (current_token = g_list_first(tokens); current_token;
5916  current_token = current_token->next)
5917  {
5918  /* Jump to next iteration if the pointer is not valid or if the
5919  string is empty. In HBCI import we almost always get an empty
5920  string, which doesn't work in the kvp loopkup later. So we
5921  skip this case here. */
5922  if (!current_token->data || (*((char*)current_token->data) == '\0'))
5923  continue;
5924  /* start off with one token for this account */
5925  token_count = 1;
5926  PINFO("adding token '%s'", (char*)current_token->data);
5927  auto path = std::string {IMAP_FRAME_BAYES} + '/' + static_cast<char*>(current_token->data) + '/' + guid_string;
5928  /* change the imap entry for the account */
5929  change_imap_entry (imap, path, token_count);
5930  }
5931  /* free up the account fullname and guid string */
5932  qof_instance_set_dirty (QOF_INSTANCE (imap->acc));
5933  xaccAccountCommitEdit (imap->acc);
5934  g_free (account_fullname);
5935  g_free (guid_string);
5936  LEAVE(" ");
5937 }
5938 
5939 /*******************************************************************************/
5940 
5941 static void
5942 build_non_bayes (const char *key, const GValue *value, gpointer user_data)
5943 {
5944  if (!G_VALUE_HOLDS_BOXED (value))
5945  return;
5946  QofBook *book;
5947  GncGUID *guid = NULL;
5948  gchar *guid_string = NULL;
5949  auto imapInfo = (GncImapInfo*)user_data;
5950  // Get the book
5951  book = qof_instance_get_book (imapInfo->source_account);
5952 
5953  guid = (GncGUID*)g_value_get_boxed (value);
5954  guid_string = guid_to_string (guid);
5955 
5956  PINFO("build_non_bayes: match string '%s', match account guid: '%s'",
5957  (char*)key, guid_string);
5958 
5959  auto imapInfo_node = static_cast <GncImapInfo*> (g_malloc(sizeof(GncImapInfo)));
5960 
5961  imapInfo_node->source_account = imapInfo->source_account;
5962  imapInfo_node->map_account = xaccAccountLookup (guid, book);
5963  imapInfo_node->head = g_strdup (imapInfo->head);
5964  imapInfo_node->match_string = g_strdup (key);
5965  imapInfo_node->category = g_strdup (imapInfo->category);
5966  imapInfo_node->count = g_strdup (" ");
5967 
5968  imapInfo->list = g_list_prepend (imapInfo->list, imapInfo_node);
5969 
5970  g_free (guid_string);
5971 }
5972 
5973 static void
5974 build_bayes (const char *suffix, KvpValue * value, GncImapInfo & imapInfo)
5975 {
5976  size_t guid_start = strlen(suffix) - GUID_ENCODING_LENGTH;
5977  std::string account_guid {&suffix[guid_start]};
5978  GncGUID guid;
5979  try
5980  {
5981  guid = gnc::GUID::from_string (account_guid);
5982  }
5983  catch (const gnc::guid_syntax_exception& err)
5984  {
5985  PWARN("Invalid GUID string from %s%s", IMAP_FRAME_BAYES, suffix);
5986  }
5987  auto map_account = xaccAccountLookup (&guid, gnc_account_get_book (imapInfo.source_account));
5988  auto imap_node = static_cast <GncImapInfo*> (g_malloc (sizeof (GncImapInfo)));
5989  auto count = value->get <int64_t> ();
5990  imap_node->source_account = imapInfo.source_account;
5991  imap_node->map_account = map_account;
5992  imap_node->head = g_strdup_printf ("%s%s", IMAP_FRAME_BAYES, suffix);
5993  imap_node->match_string = g_strndup (&suffix[1], guid_start - 2);
5994  imap_node->category = g_strdup(" ");
5995  imap_node->count = g_strdup_printf ("%" G_GINT64_FORMAT, count);
5996  imapInfo.list = g_list_prepend (imapInfo.list, imap_node);
5997 }
5998 
5999 GList *
6001 {
6002  check_import_map_data (gnc_account_get_book (acc));
6003  /* A dummy object which is used to hold the specified account, and the list
6004  * of data about which we care. */
6005  GncImapInfo imapInfo {acc, nullptr};
6006  qof_instance_foreach_slot_prefix (QOF_INSTANCE (acc), IMAP_FRAME_BAYES, &build_bayes, imapInfo);
6007  return g_list_reverse(imapInfo.list);
6008 }
6009 
6010 GList *
6011 gnc_account_imap_get_info (Account *acc, const char *category)
6012 {
6013  GList *list = NULL;
6014 
6015  GncImapInfo imapInfo;
6016 
6017  std::vector<std::string> path {IMAP_FRAME};
6018  if (category)
6019  path.emplace_back (category);
6020 
6021  imapInfo.source_account = acc;
6022  imapInfo.list = list;
6023 
6024  imapInfo.head = g_strdup (IMAP_FRAME);
6025  imapInfo.category = g_strdup (category);
6026 
6027  if (qof_instance_has_path_slot (QOF_INSTANCE (acc), path))
6028  {
6029  qof_instance_foreach_slot (QOF_INSTANCE(acc), IMAP_FRAME, category,
6030  build_non_bayes, &imapInfo);
6031  }
6032  g_free (imapInfo.head);
6033  g_free (imapInfo.category);
6034  return g_list_reverse(imapInfo.list);
6035 }
6036 
6037 /*******************************************************************************/
6038 
6039 gchar *
6040 gnc_account_get_map_entry (Account *acc, const char *head, const char *category)
6041 {
6042  if (category)
6043  return get_kvp_string_path (acc, {head, category});
6044  else
6045  return get_kvp_string_path (acc, {head});
6046 }
6047 
6048 
6049 void
6050 gnc_account_delete_map_entry (Account *acc, char *head, char *category,
6051  char *match_string, gboolean empty)
6052 {
6053  if (acc != NULL)
6054  {
6055  std::vector<std::string> path {head};
6056  if (category)
6057  path.emplace_back (category);
6058  if (match_string)
6059  path.emplace_back (match_string);
6060 
6061  if (qof_instance_has_path_slot (QOF_INSTANCE (acc), path))
6062  {
6063  xaccAccountBeginEdit (acc);
6064  if (empty)
6065  qof_instance_slot_path_delete_if_empty (QOF_INSTANCE(acc), path);
6066  else
6067  qof_instance_slot_path_delete (QOF_INSTANCE(acc), path);
6068  PINFO("Account is '%s', head is '%s', category is '%s', match_string is'%s'",
6069  xaccAccountGetName (acc), head, category, match_string);
6070  qof_instance_set_dirty (QOF_INSTANCE(acc));
6071  xaccAccountCommitEdit (acc);
6072  }
6073  }
6074 }
6075 
6076 void
6078 {
6079  if (acc != NULL)
6080  {
6081  auto slots = qof_instance_get_slots_prefix (QOF_INSTANCE (acc), IMAP_FRAME_BAYES);
6082  if (!slots.size()) return;
6083  for (auto const & entry : slots)
6084  {
6085  qof_instance_slot_path_delete (QOF_INSTANCE (acc), {entry.first});
6086  }
6087  }
6088 }
6089 
6090 /* ================================================================ */
6091 /* QofObject function implementation and registration */
6092 
6093 static void
6094 gnc_account_book_end(QofBook* book)
6095 {
6096  Account *root_account = gnc_book_get_root_account(book);
6097  if (!root_account)
6098  return;
6099  xaccAccountBeginEdit(root_account);
6100  xaccAccountDestroy(root_account);
6101 }
6102 
6103 #ifdef _MSC_VER
6104 /* MSVC compiler doesn't have C99 "designated initializers"
6105  * so we wrap them in a macro that is empty on MSVC. */
6106 # define DI(x) /* */
6107 #else
6108 # define DI(x) x
6109 #endif
6110 static QofObject account_object_def =
6111 {
6112  DI(.interface_version = ) QOF_OBJECT_VERSION,
6113  DI(.e_type = ) GNC_ID_ACCOUNT,
6114  DI(.type_label = ) "Account",
6115  DI(.create = ) (void*(*)(QofBook*)) xaccMallocAccount,
6116  DI(.book_begin = ) NULL,
6117  DI(.book_end = ) gnc_account_book_end,
6118  DI(.is_dirty = ) qof_collection_is_dirty,
6119  DI(.mark_clean = ) qof_collection_mark_clean,
6120  DI(.foreach = ) qof_collection_foreach,
6121  DI(.printable = ) (const char * (*)(gpointer)) xaccAccountGetName,
6122  DI(.version_cmp = ) (int (*)(gpointer, gpointer)) qof_instance_version_cmp,
6123 };
6124 
6125 gboolean xaccAccountRegister (void)
6126 {
6127  static QofParam params[] =
6128  {
6129  {
6130  ACCOUNT_NAME_, QOF_TYPE_STRING,
6133  },
6134  {
6135  ACCOUNT_CODE_, QOF_TYPE_STRING,
6138  },
6139  {
6140  ACCOUNT_DESCRIPTION_, QOF_TYPE_STRING,
6143  },
6144  {
6145  ACCOUNT_COLOR_, QOF_TYPE_STRING,
6148  },
6149  {
6150  ACCOUNT_FILTER_, QOF_TYPE_STRING,
6153  },
6154  {
6155  ACCOUNT_SORT_ORDER_, QOF_TYPE_STRING,
6158  },
6159  {
6160  ACCOUNT_SORT_REVERSED_, QOF_TYPE_BOOLEAN,
6163  },
6164  {
6165  ACCOUNT_NOTES_, QOF_TYPE_STRING,
6168  },
6169  {
6170  ACCOUNT_PRESENT_, QOF_TYPE_NUMERIC,
6171  (QofAccessFunc) xaccAccountGetPresentBalance, NULL
6172  },
6173  {
6174  ACCOUNT_BALANCE_, QOF_TYPE_NUMERIC,
6176  },
6177  {
6178  ACCOUNT_CLEARED_, QOF_TYPE_NUMERIC,
6180  },
6181  {
6182  ACCOUNT_RECONCILED_, QOF_TYPE_NUMERIC,
6184  },
6185  {
6186  ACCOUNT_TYPE_, QOF_TYPE_STRING,
6187  (QofAccessFunc) qofAccountGetTypeString,
6188  (QofSetterFunc) qofAccountSetType
6189  },
6190  {
6191  ACCOUNT_FUTURE_MINIMUM_, QOF_TYPE_NUMERIC,
6192  (QofAccessFunc) xaccAccountGetProjectedMinimumBalance, NULL
6193  },
6194  {
6195  ACCOUNT_TAX_RELATED, QOF_TYPE_BOOLEAN,
6198  },
6199  {
6200  ACCOUNT_OPENING_BALANCE_, QOF_TYPE_BOOLEAN,
6203  },
6204  {
6205  ACCOUNT_SCU, QOF_TYPE_INT32,
6208  },
6209  {
6210  ACCOUNT_NSCU, QOF_TYPE_BOOLEAN,
6213  },
6214  {
6215  ACCOUNT_PARENT, GNC_ID_ACCOUNT,
6217  (QofSetterFunc) qofAccountSetParent
6218  },
6219  {
6220  QOF_PARAM_BOOK, QOF_ID_BOOK,
6222  },
6223  {
6224  QOF_PARAM_GUID, QOF_TYPE_GUID,
6226  },
6227  { NULL },
6228  };
6229 
6230  qof_class_register (GNC_ID_ACCOUNT, (QofSortFunc) qof_xaccAccountOrder, params);
6231 
6232  return qof_object_register (&account_object_def);
6233 }
6234 
6235 using AccountSet = std::unordered_set<Account*>;
6236 static void maybe_add_descendants (Account* acc, gpointer arg)
6237 {
6238  if (static_cast <AccountSet*> (arg)->insert (acc).second)
6239  g_list_foreach (GET_PRIVATE(acc)->children, (GFunc) maybe_add_descendants, arg);
6240 };
6241 
6242 GList *
6243 gnc_accounts_and_all_descendants (GList *accounts)
6244 {
6245  AccountSet accset;
6246  g_list_foreach (accounts, (GFunc) maybe_add_descendants, &accset);
6247  return std::accumulate (accset.begin(), accset.end(), (GList*) nullptr, g_list_prepend);
6248 }
6249 
6250 /* ======================= UNIT TESTING ACCESS =======================
6251  * The following functions are for unit testing use only.
6252  */
6253 static AccountPrivate*
6254 utest_account_get_private (Account *acc)
6255 {
6256  return GET_PRIVATE (acc);
6257 }
6258 
6260 _utest_account_fill_functions(void)
6261 {
6262  AccountTestFunctions* func = g_new(AccountTestFunctions, 1);
6263 
6264  func->get_private = utest_account_get_private;
6265  func->coll_get_root_account = gnc_coll_get_root_account;
6266  func->xaccFreeAccountChildren = xaccFreeAccountChildren;
6267  func->xaccFreeAccount = xaccFreeAccount;
6268  func->qofAccountSetParent = qofAccountSetParent;
6269  func->gnc_account_lookup_by_full_name_helper =
6270  gnc_account_lookup_by_full_name_helper;
6271 
6272  return func;
6273 }
6274 /* ======================= 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:5437
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:5380
gboolean xaccAccountGetAutoInterest(const Account *acc)
Get the "auto interest" flag for an account.
Definition: Account.cpp:4315
holds an account guid and its corresponding integer probability the integer probability is some facto...
Definition: Account.cpp:5573
const char * xaccAccountGetLastNum(const Account *acc)
Get the last num field of an Account.
Definition: Account.cpp:4912
GNCAccountType xaccAccountTypeGetFundamental(GNCAccountType t)
Convenience function to return the fundamental type asset/liability/income/expense/equity given an ac...
Definition: Account.cpp:4635
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:6040
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:4507
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:4175
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:4691
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:6050
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:4253
void xaccAccountSetReconcileLastDate(Account *acc, time64 last_date)
DOCUMENT ME!
Definition: Account.cpp:4731
total_count and the token_count for a given account let us calculate the probability of a given accou...
Definition: Account.cpp:5564
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:4131
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:134
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:4783
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:4612
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:4387
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:4213
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:4924
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:5826
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:4807
intermediate values used to calculate the bayes probability of a given account where p(AB) = (a*b)/[a...
Definition: Account.cpp:5549
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:4342
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:5152
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:4621
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:4458
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:5889
void xaccAccountSetPlaceholder(Account *acc, gboolean val)
Set the "placeholder" flag for an account.
Definition: Account.cpp:4247
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:4594
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:5303
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:5167
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:5330
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:5019
#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:5292
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:4291
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:6077
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:5058
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:4898
const char * xaccAccountGetTaxUSPayerNameSource(const Account *acc)
DOCUMENT ME!
Definition: Account.cpp:4156
void xaccSplitsBeginStagedTransactionTraversals(GList *splits)
xaccSplitsBeginStagedTransactionTraversals() resets the traversal marker for each transaction which i...
Definition: Account.cpp:5276
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:5794
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:4029
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:6000
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:6011
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:4832
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:4137
gboolean xaccAccountIsAPARType(GNCAccountType t)
Convenience function to check if the account is a valid business account type (meaning an Accounts Pa...
Definition: Account.cpp:4667
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:4424
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:4281
void xaccAccountSetReconcilePostponeBalance(Account *acc, gnc_numeric balance)
DOCUMENT ME!
Definition: Account.cpp:4878
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:4125
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:5180
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:4706
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:5047
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:4330
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:4259
GncImportMatchMap * gnc_account_imap_create_imap(Account *acc)
Obtain an ImportMatchMap object from an Account or a Book.
Definition: Account.cpp:5456
GLib helper routines.
Generic api to store and retrieve preferences.
gnc_numeric gnc_numeric_sub(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Return a-b.
gboolean gnc_account_insert_split(Account *acc, Split *s)
Insert the given split from an account.
Definition: Account.cpp: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:4321
void xaccAccountSetTaxUSCode(Account *acc, const char *code)
DOCUMENT ME!
Definition: Account.cpp:4146
GNCPlaceholderType
DOCUMENT ME!
Definition: Account.h:1181
gboolean xaccAccountGetIsOpeningBalance(const Account *acc)
Get the "opening-balance" flag for an account.
Definition: Account.cpp:4265
guint32 xaccParentAccountTypesCompatibleWith(GNCAccountType type)
Return the bitmask of parent account types compatible with a given type.
Definition: Account.cpp:4547
gboolean xaccAccountGetReconcileChildrenStatus(const Account *acc)
DOCUMENT ME!
Definition: Account.cpp:5095
gboolean xaccAccountGetReconcileLastInterval(const Account *acc, int *months, int *days)
DOCUMENT ME!
Definition: Account.cpp:4749
time64 xaccSplitGetDateReconciled(const Split *split)
Retrieve the date when the Split was reconciled.
Definition: Split.c:1824
Not a type.
Definition: Account.h:107
const char * dxaccAccountGetPriceSrc(const Account *acc)
Get a string that identifies the Finance::Quote backend that should be used to retrieve online prices...
Definition: Account.cpp:5031
liability (and asset) accounts indicate generic, generalized accounts that are none of the above...
Definition: Account.h:122
const char * gnc_account_get_credit_string(GNCAccountType acct_type)
Get the credit string associated with this account type.
Definition: Account.cpp:4225
gboolean gnc_lot_is_closed(GNC