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