~siretart/gnucash/ubuntu-fullsource

« back to all changes in this revision

Viewing changes to src/engine/Account.c

  • Committer: Reinhard Tartler
  • Date: 2008-08-03 07:25:46 UTC
  • Revision ID: siretart@tauware.de-20080803072546-y6p8xda8zpfi62ys
import gnucash_2.2.4.orig.tar.gz

The original tarball had the md5sum: 27e660297dc5b8ce574515779d05a5a5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/********************************************************************\
 
2
 * Account.c -- Account data structure implementation               *
 
3
 * Copyright (C) 1997 Robin D. Clark                                *
 
4
 * Copyright (C) 1997-2003 Linas Vepstas <linas@linas.org>          *
 
5
 * Copyright (C) 2007 David Hampton <hampton@employees.org>         *
 
6
 *                                                                  *
 
7
 * This program is free software; you can redistribute it and/or    *
 
8
 * modify it under the terms of the GNU General Public License as   *
 
9
 * published by the Free Software Foundation; either version 2 of   *
 
10
 * the License, or (at your option) any later version.              *
 
11
 *                                                                  *
 
12
 * This program is distributed in the hope that it will be useful,  *
 
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
 
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
 
15
 * GNU General Public License for more details.                     *
 
16
 *                                                                  *
 
17
 * You should have received a copy of the GNU General Public License*
 
18
 * along with this program; if not, contact:                        *
 
19
 *                                                                  *
 
20
 * Free Software Foundation           Voice:  +1-617-542-5942       *
 
21
 * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
 
22
 * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
 
23
 *                                                                  *
 
24
\********************************************************************/
 
25
 
 
26
#include "config.h"
 
27
 
 
28
#include <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-lot-p.h"
 
41
#include "gnc-pricedb.h"
 
42
 
 
43
#define GNC_ID_ROOT_ACCOUNT        "RootAccount"
 
44
 
 
45
static QofLogModule log_module = GNC_MOD_ACCOUNT;
 
46
 
 
47
/* The Canonical Account Separator.  Pre-Initialized. */
 
48
static gchar account_separator[8] = ".";
 
49
gunichar account_uc_separator = ':';
 
50
 
 
51
enum {
 
52
    LAST_SIGNAL
 
53
};
 
54
 
 
55
enum {
 
56
  PROP_0,
 
57
  PROP_NAME,
 
58
  PROP_FULL_NAME,
 
59
  PROP_CODE,
 
60
  PROP_DESCRIPTION,
 
61
  PROP_NOTES,
 
62
  PROP_TYPE,
 
63
 
 
64
  PROP_COMMODITY,
 
65
  PROP_COMMODITY_SCU,
 
66
  PROP_NON_STD_SCU,
 
67
  PROP_SORT_DIRTY,
 
68
  PROP_BALANCE_DIRTY,
 
69
  PROP_START_BALANCE,
 
70
  PROP_START_CLEARED_BALANCE,
 
71
  PROP_START_RECONCILED_BALANCE,
 
72
  PROP_END_BALANCE,
 
73
  PROP_END_CLEARED_BALANCE,
 
74
  PROP_END_RECONCILED_BALANCE,
 
75
 
 
76
  PROP_POLICY,
 
77
  PROP_MARK,
 
78
  PROP_TAX_RELATED,
 
79
  PROP_TAX_CODE,
 
80
  PROP_TAX_SOURCE,
 
81
};
 
82
 
 
83
typedef struct AccountPrivate
 
84
{
 
85
    /* The accountName is an arbitrary string assigned by the user. 
 
86
     * It is intended to a short, 5 to 30 character long string that
 
87
     * is displayed by the GUI as the account mnemonic. 
 
88
     */
 
89
    char *accountName;
 
90
 
 
91
    /* The accountCode is an arbitrary string assigned by the user.
 
92
     * It is intended to be reporting code that is a synonym for the 
 
93
     * accountName. Typically, it will be a numeric value that follows 
 
94
     * the numbering assignments commonly used by accountants, such 
 
95
     * as 100, 200 or 600 for top-level accounts, and 101, 102..  etc.
 
96
     * for detail accounts.
 
97
     */
 
98
    char *accountCode;
 
99
 
 
100
    /* The description is an arbitrary string assigned by the user. 
 
101
     * It is intended to be a longer, 1-5 sentence description of what
 
102
     * this account is all about.
 
103
     */
 
104
    char *description;
 
105
 
 
106
    /* The type field is the account type, picked from the enumerated
 
107
     * list that includes ACCT_TYPE_BANK, ACCT_TYPE_STOCK,
 
108
     * ACCT_TYPE_CREDIT, ACCT_TYPE_INCOME, etc.  Its intended use is to
 
109
     * be a hint to the GUI as to how to display and format the
 
110
     * transaction data.
 
111
     */
 
112
    GNCAccountType type;
 
113
 
 
114
    /* 
 
115
     * The commodity field denotes the kind of 'stuff' stored 
 
116
     * in this account.  The 'amount' field of a split indicates
 
117
     * how much of the 'stuff' there is.
 
118
     */
 
119
    gnc_commodity * commodity;
 
120
    int commodity_scu;
 
121
    gboolean non_standard_scu;
 
122
 
 
123
    /* The parent and children pointers are used to implement an account
 
124
     * hierarchy, of accounts that have sub-accounts ("detail accounts").
 
125
     */
 
126
    Account *parent;    /* back-pointer to parent */
 
127
    GList *children;    /* list of sub-accounts */
 
128
 
 
129
    /* protected data - should only be set by backends */
 
130
    gnc_numeric starting_balance;
 
131
    gnc_numeric starting_cleared_balance;
 
132
    gnc_numeric starting_reconciled_balance;
 
133
 
 
134
    /* cached parameters */
 
135
    gnc_numeric balance;
 
136
    gnc_numeric cleared_balance;
 
137
    gnc_numeric reconciled_balance;
 
138
 
 
139
    gboolean balance_dirty;     /* balances in splits incorrect */
 
140
 
 
141
    GList *splits;              /* list of split pointers */
 
142
    gboolean sort_dirty;        /* sort order of splits is bad */
 
143
 
 
144
    LotList   *lots;            /* list of lot pointers */
 
145
    GNCPolicy *policy;          /* Cached pointer to policy method */
 
146
 
 
147
    /* The "mark" flag can be used by the user to mark this account
 
148
     * in any way desired.  Handy for specialty traversals of the 
 
149
     * account tree. */
 
150
    short mark;
 
151
} AccountPrivate;
 
152
 
 
153
#define GET_PRIVATE(o)  \
 
154
   (G_TYPE_INSTANCE_GET_PRIVATE ((o), GNC_TYPE_ACCOUNT, AccountPrivate))
 
155
 
 
156
/********************************************************************\
 
157
 * Because I can't use C++ for this project, doesn't mean that I    *
 
158
 * can't pretend to!  These functions perform actions on the        *
 
159
 * account data structure, in order to encapsulate the knowledge    *
 
160
 * of the internals of the Account in one file.                     *
 
161
\********************************************************************/
 
162
 
 
163
static void xaccAccountBringUpToDate (Account *acc);
 
164
 
 
165
 
 
166
/********************************************************************\
 
167
 * gnc_get_account_separator                                        *
 
168
 *   returns the current account separator character                *
 
169
 *                                                                  *
 
170
 * Args: none                                                       *
 
171
 * Returns: account separator character                             *
 
172
 \*******************************************************************/
 
173
const gchar *
 
174
gnc_get_account_separator_string (void)
 
175
{
 
176
  return account_separator;
 
177
}
 
178
 
 
179
gunichar
 
180
gnc_get_account_separator (void)
 
181
{
 
182
  return account_uc_separator;
 
183
}
 
184
 
 
185
void
 
186
gnc_set_account_separator (const gchar *separator)
 
187
{
 
188
  gunichar uc;
 
189
  gint count;
 
190
 
 
191
  uc = g_utf8_get_char_validated(separator, -1);
 
192
  if ((uc == (gunichar)-2) || (uc == (gunichar)-1) || g_unichar_isalnum(uc)) {
 
193
    account_uc_separator = ':';
 
194
    strcpy(account_separator, ":");
 
195
    return;
 
196
  }
 
197
 
 
198
  account_uc_separator = uc;
 
199
  count = g_unichar_to_utf8(uc, account_separator);
 
200
  account_separator[count] = '\0';
 
201
}
 
202
 
 
203
/********************************************************************\
 
204
\********************************************************************/
 
205
 
 
206
G_INLINE_FUNC void mark_account (Account *acc);
 
207
void
 
208
mark_account (Account *acc)
 
209
{
 
210
  qof_instance_set_dirty(&acc->inst);
 
211
}
 
212
 
 
213
/********************************************************************\
 
214
\********************************************************************/
 
215
 
 
216
/* GObject Initialization */
 
217
G_DEFINE_TYPE(Account, gnc_account, QOF_TYPE_INSTANCE)
 
218
 
 
219
static void
 
220
gnc_account_init(Account* acc)
 
221
{
 
222
    AccountPrivate *priv;
 
223
 
 
224
    priv = GET_PRIVATE(acc);
 
225
    priv->parent   = NULL;
 
226
    priv->children = NULL;
 
227
 
 
228
    priv->accountName = CACHE_INSERT("");
 
229
    priv->accountCode = CACHE_INSERT("");
 
230
    priv->description = CACHE_INSERT("");
 
231
 
 
232
    priv->type = ACCT_TYPE_NONE;
 
233
 
 
234
    priv->mark = 0;
 
235
 
 
236
    priv->policy = xaccGetFIFOPolicy();
 
237
    priv->lots = NULL;
 
238
 
 
239
    priv->commodity = NULL;
 
240
    priv->commodity_scu = 0;
 
241
    priv->non_standard_scu = FALSE;
 
242
 
 
243
    priv->balance = gnc_numeric_zero();
 
244
    priv->cleared_balance = gnc_numeric_zero();
 
245
    priv->reconciled_balance = gnc_numeric_zero();
 
246
    priv->starting_balance = gnc_numeric_zero();
 
247
    priv->starting_cleared_balance = gnc_numeric_zero();
 
248
    priv->starting_reconciled_balance = gnc_numeric_zero();
 
249
    priv->balance_dirty = FALSE;
 
250
 
 
251
    priv->splits = NULL;
 
252
    priv->sort_dirty = FALSE;
 
253
 }
 
254
 
 
255
static void
 
256
gnc_account_dispose (GObject *acctp)
 
257
{
 
258
    G_OBJECT_CLASS(gnc_account_parent_class)->dispose(acctp);
 
259
}
 
260
 
 
261
static void
 
262
gnc_account_finalize(GObject* acctp)
 
263
{
 
264
    G_OBJECT_CLASS(gnc_account_parent_class)->finalize(acctp);
 
265
}
 
266
 
 
267
static void
 
268
gnc_account_get_property (GObject         *object,
 
269
                          guint            prop_id,
 
270
                          GValue          *value,
 
271
                          GParamSpec      *pspec)
 
272
{
 
273
    Account *account;
 
274
    AccountPrivate *priv;
 
275
 
 
276
    g_return_if_fail(GNC_IS_ACCOUNT(object));
 
277
 
 
278
    account = GNC_ACCOUNT(object);
 
279
    priv = GET_PRIVATE(account);
 
280
    switch (prop_id) {
 
281
        case PROP_NAME:
 
282
            g_value_set_string(value, priv->accountName);
 
283
            break;
 
284
        case PROP_FULL_NAME:
 
285
            g_value_take_string(value, xaccAccountGetFullName(account));
 
286
            break;
 
287
        case PROP_CODE:
 
288
            g_value_set_string(value, priv->accountCode);
 
289
            break;
 
290
        case PROP_DESCRIPTION:
 
291
            g_value_set_string(value, priv->description);
 
292
            break;
 
293
        case PROP_NOTES:
 
294
            g_value_set_string(value, xaccAccountGetNotes(account));
 
295
            break;
 
296
        case PROP_TYPE:
 
297
            // NEED TO BE CONVERTED TO A G_TYPE_ENUM
 
298
            g_value_set_int(value, priv->type);
 
299
            break;
 
300
        case PROP_COMMODITY:
 
301
            g_value_set_object(value, priv->commodity);
 
302
            break;
 
303
        case PROP_COMMODITY_SCU:
 
304
            g_value_set_int(value, priv->commodity_scu);
 
305
            break;
 
306
        case PROP_NON_STD_SCU:
 
307
            g_value_set_boolean(value, priv->non_standard_scu);
 
308
            break;
 
309
        case PROP_SORT_DIRTY:
 
310
            g_value_set_boolean(value, priv->sort_dirty);
 
311
            break;
 
312
        case PROP_BALANCE_DIRTY:
 
313
            g_value_set_boolean(value, priv->balance_dirty);
 
314
            break;
 
315
        case PROP_START_BALANCE:
 
316
            g_value_set_boxed(value, &priv->starting_balance);
 
317
            break;
 
318
        case PROP_START_CLEARED_BALANCE:
 
319
            g_value_set_boxed(value, &priv->starting_cleared_balance);
 
320
            break;
 
321
        case PROP_START_RECONCILED_BALANCE:
 
322
            g_value_set_boxed(value, &priv->starting_reconciled_balance);
 
323
            break;
 
324
        case PROP_END_BALANCE:
 
325
            g_value_set_boxed(value, &priv->balance);
 
326
            break;
 
327
        case PROP_END_CLEARED_BALANCE:
 
328
            g_value_set_boxed(value, &priv->cleared_balance);
 
329
            break;
 
330
        case PROP_END_RECONCILED_BALANCE:
 
331
            g_value_set_boxed(value, &priv->reconciled_balance);
 
332
            break;
 
333
        case PROP_POLICY:
 
334
            /* MAKE THIS A BOXED VALUE */
 
335
            g_value_set_pointer(value, priv->policy);
 
336
            break;
 
337
        case PROP_MARK:
 
338
            g_value_set_int(value, priv->mark);
 
339
            break;
 
340
        case PROP_TAX_RELATED:
 
341
            g_value_set_boolean(value, xaccAccountGetTaxRelated(account));
 
342
            break;
 
343
        case PROP_TAX_CODE:
 
344
            g_value_set_string(value, xaccAccountGetTaxUSCode(account));
 
345
            break;
 
346
        case PROP_TAX_SOURCE:
 
347
            g_value_set_string(value,
 
348
                               xaccAccountGetTaxUSPayerNameSource(account));
 
349
            break;
 
350
        default:
 
351
            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
 
352
            break;
 
353
    }
 
354
}
 
355
 
 
356
static void
 
357
gnc_account_set_property (GObject         *object,
 
358
                          guint            prop_id,
 
359
                          const GValue    *value,
 
360
                          GParamSpec      *pspec)
 
361
{
 
362
    Account *account;
 
363
    gnc_numeric *number;
 
364
 
 
365
    g_return_if_fail(GNC_IS_ACCOUNT(object));
 
366
 
 
367
    account = GNC_ACCOUNT(object);
 
368
 
 
369
    switch (prop_id) {
 
370
        case PROP_NAME:
 
371
            xaccAccountSetName(account, g_value_get_string(value));
 
372
            break;
 
373
        case PROP_CODE:
 
374
            xaccAccountSetCode(account, g_value_get_string(value));
 
375
            break;
 
376
        case PROP_DESCRIPTION:
 
377
            xaccAccountSetDescription(account, g_value_get_string(value));
 
378
            break;
 
379
        case PROP_NOTES:
 
380
            xaccAccountSetNotes(account, g_value_get_string(value));
 
381
            break;
 
382
        case PROP_TYPE:
 
383
            // NEED TO BE CONVERTED TO A G_TYPE_ENUM
 
384
            xaccAccountSetType(account, g_value_get_int(value));
 
385
            break;
 
386
        case PROP_COMMODITY:
 
387
            xaccAccountSetCommodity(account, g_value_get_object(value));
 
388
            break;
 
389
        case PROP_COMMODITY_SCU:
 
390
            xaccAccountSetCommoditySCU(account, g_value_get_int(value));
 
391
            break;
 
392
        case PROP_SORT_DIRTY:
 
393
            gnc_account_set_sort_dirty(account);
 
394
            break;
 
395
        case PROP_BALANCE_DIRTY:
 
396
            gnc_account_set_balance_dirty(account);
 
397
            break;
 
398
        case PROP_START_BALANCE:
 
399
            number = g_value_get_boxed(value);
 
400
            gnc_account_set_start_balance(account, *number);
 
401
            break;
 
402
        case PROP_START_CLEARED_BALANCE:
 
403
            number = g_value_get_boxed(value);
 
404
            gnc_account_set_start_cleared_balance(account, *number);
 
405
            break;
 
406
        case PROP_START_RECONCILED_BALANCE:
 
407
            number = g_value_get_boxed(value);
 
408
            gnc_account_set_start_reconciled_balance(account, *number);
 
409
            break;
 
410
        case PROP_POLICY:
 
411
            gnc_account_set_policy(account, g_value_get_pointer(value));
 
412
            break;
 
413
        case PROP_MARK:
 
414
            xaccAccountSetMark(account, g_value_get_int(value));
 
415
            break;
 
416
        case PROP_TAX_RELATED:
 
417
            xaccAccountSetTaxRelated(account, g_value_get_boolean(value));
 
418
            break;
 
419
        case PROP_TAX_CODE:
 
420
            xaccAccountSetTaxUSCode(account, g_value_get_string(value));
 
421
            break;
 
422
        case PROP_TAX_SOURCE:
 
423
            xaccAccountSetTaxUSPayerNameSource(account,
 
424
                                               g_value_get_string(value));
 
425
        default:
 
426
            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
 
427
            break;
 
428
    }    
 
429
}
 
430
 
 
431
 
 
432
 
 
433
static void
 
434
gnc_account_class_init (AccountClass *klass)
 
435
{
 
436
    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
437
        
 
438
    gobject_class->dispose = gnc_account_dispose;
 
439
    gobject_class->finalize = gnc_account_finalize;
 
440
    gobject_class->set_property = gnc_account_set_property;
 
441
    gobject_class->get_property = gnc_account_get_property;
 
442
 
 
443
    g_type_class_add_private(klass, sizeof(AccountPrivate));
 
444
 
 
445
    g_object_class_install_property
 
446
        (gobject_class,
 
447
         PROP_NAME,
 
448
         g_param_spec_string ("name",
 
449
                              "Account Name",
 
450
                              "The accountName is an arbitrary string "
 
451
                              "assigned by the user.  It is intended to "
 
452
                              "a short, 5 to 30 character long string "
 
453
                              "that is displayed by the GUI as the "
 
454
                              "account mnemonic.  Account names may be "
 
455
                              "repeasted. but no two accounts that share "
 
456
                              "a parent may have the same name.",
 
457
                              NULL,
 
458
                              G_PARAM_READWRITE));
 
459
 
 
460
    g_object_class_install_property
 
461
        (gobject_class,
 
462
         PROP_FULL_NAME,
 
463
         g_param_spec_string ("fullname",
 
464
                              "Full Account Name",
 
465
                              "The name of the account concatenated with "
 
466
                              "all its parent account names to indicate "
 
467
                              "a unique account.",
 
468
                              NULL,
 
469
                              G_PARAM_READABLE));
 
470
 
 
471
    g_object_class_install_property
 
472
        (gobject_class,
 
473
         PROP_CODE,
 
474
         g_param_spec_string ("code",
 
475
                              "Account Code",
 
476
                              "The account code is an arbitrary string "
 
477
                              "assigned by the user. It is intended to "
 
478
                              "be reporting code that is a synonym for "
 
479
                              "the accountName.",
 
480
                              NULL,
 
481
                              G_PARAM_READWRITE));
 
482
 
 
483
    g_object_class_install_property
 
484
        (gobject_class,
 
485
         PROP_DESCRIPTION,
 
486
         g_param_spec_string ("description",
 
487
                              "Account Description",
 
488
                              "The account description is an arbitrary "
 
489
                              "string assigned by the user. It is intended "
 
490
                              "to be a longer, 1-5 sentence description of "
 
491
                              "what this account is all about.",
 
492
                              NULL,
 
493
                              G_PARAM_READWRITE));
 
494
 
 
495
    g_object_class_install_property
 
496
        (gobject_class,
 
497
         PROP_NOTES,
 
498
         g_param_spec_string ("notes",
 
499
                              "Account Notes",
 
500
                              "The account notes is an arbitrary provided "
 
501
                              "for the user to attach any orther text that "
 
502
                              "they would like to associate with the account.",
 
503
                              NULL,
 
504
                              G_PARAM_READWRITE));
 
505
 
 
506
    g_object_class_install_property
 
507
        (gobject_class,
 
508
         PROP_TYPE,
 
509
         g_param_spec_int ("type",
 
510
                           "Account Type",
 
511
                           "The account type, picked from the enumerated list "
 
512
                           "that includes ACCT_TYPE_BANK, ACCT_TYPE_STOCK, "
 
513
                           "ACCT_TYPE_CREDIT, ACCT_TYPE_INCOME, etc.",
 
514
                           ACCT_TYPE_NONE,
 
515
                           NUM_ACCOUNT_TYPES - 1,
 
516
                           ACCT_TYPE_BANK,
 
517
                           G_PARAM_READWRITE));
 
518
 
 
519
    g_object_class_install_property
 
520
        (gobject_class,
 
521
         PROP_COMMODITY,
 
522
         g_param_spec_object ("commodity",
 
523
                              "Commodity",
 
524
                              "The commodity field denotes the kind of "
 
525
                              "'stuff' stored  in this account, whether "
 
526
                              "it is USD, gold, stock, etc.",
 
527
                              GNC_TYPE_COMMODITY,
 
528
                              G_PARAM_READWRITE));
 
529
 
 
530
    g_object_class_install_property
 
531
        (gobject_class,
 
532
         PROP_COMMODITY_SCU,
 
533
         g_param_spec_int ("commodity-scu",
 
534
                           "Commodity SCU",
 
535
                           "The smallest fraction of the commodity that is "
 
536
                           "tracked.  This number is used as the denominator "
 
537
                           "value in 1/x, so a value of 100 says that the "
 
538
                           "commodity can be divided into hundreths.  E.G."
 
539
                           "1 USD can be divided into 100 cents.",
 
540
                           0,
 
541
                           G_MAXINT32,
 
542
                           1000000,
 
543
                           G_PARAM_READWRITE));
 
544
 
 
545
    g_object_class_install_property
 
546
        (gobject_class,
 
547
         PROP_NON_STD_SCU,
 
548
         g_param_spec_boolean ("non-std-scu",
 
549
                               "Non-std SCU",
 
550
                               "TRUE id the account SCU doesn't match "
 
551
                               "the commodity SCU.  This indicates a case "
 
552
                               "where the two were accidentally set to "
 
553
                               "mismatched values in older versions of "
 
554
                               "GnuCash.",
 
555
                               FALSE,
 
556
                               G_PARAM_READWRITE));
 
557
 
 
558
    g_object_class_install_property
 
559
        (gobject_class,
 
560
         PROP_SORT_DIRTY,
 
561
         g_param_spec_boolean("sort-dirty",
 
562
                              "Sort Dirty",
 
563
                              "TRUE if the splits in the account needs to be "
 
564
                              "resorted.  This flag is set by the accounts "
 
565
                              "code for certain internal modifications, or "
 
566
                              "when external code calls the engine to say a "
 
567
                              "split has been modified in a way that may "
 
568
                              "affect the sort order of the account. Note: "
 
569
                              "This value can only be set to TRUE.",
 
570
                              FALSE,
 
571
                              G_PARAM_READWRITE));
 
572
 
 
573
    g_object_class_install_property
 
574
        (gobject_class,
 
575
         PROP_BALANCE_DIRTY,
 
576
         g_param_spec_boolean("balance-dirty",
 
577
                              "Balance Dirty",
 
578
                              "TRUE if the running balances in the account "
 
579
                              "needs to be recalculated.  This flag is set "
 
580
                              "by the accounts code for certain internal "
 
581
                              "modifications, or when external code calls "
 
582
                              "the engine to say a split has been modified. "
 
583
                              "Note: This value can only be set to TRUE.",
 
584
                              FALSE,
 
585
                              G_PARAM_READWRITE));
 
586
 
 
587
    g_object_class_install_property
 
588
        (gobject_class,
 
589
         PROP_START_BALANCE,
 
590
         g_param_spec_boxed("start-balance",
 
591
                            "Starting Balance",
 
592
                            "The starting balance for the account.  This "
 
593
                            "parameter is intended for use with backends that "
 
594
                            "do not return the complete list of splits for an "
 
595
                            "account, but rather return a partial list.  In "
 
596
                            "such a case, the backend will typically return "
 
597
                            "all of the splits after some certain date, and "
 
598
                            "the 'starting balance' will represent the "
 
599
                            "summation of the splits up to that date.",
 
600
                            GNC_TYPE_NUMERIC,
 
601
                            G_PARAM_READWRITE));
 
602
 
 
603
    g_object_class_install_property
 
604
        (gobject_class,
 
605
         PROP_START_CLEARED_BALANCE,
 
606
         g_param_spec_boxed("start-cleared-balance",
 
607
                            "Starting Cleared Balance",
 
608
                            "The starting cleared balance for the account.  "
 
609
                            "This parameter is intended for use with backends "
 
610
                            "that do not return the complete list of splits "
 
611
                            "for an account, but rather return a partial "
 
612
                            "list.  In such a case, the backend will "
 
613
                            "typically return all of the splits after "
 
614
                            "some certain date, and the 'starting cleared "
 
615
                            "balance' will represent the summation of the "
 
616
                            "splits up to that date.",
 
617
                            GNC_TYPE_NUMERIC,
 
618
                            G_PARAM_READWRITE));
 
619
 
 
620
    g_object_class_install_property
 
621
        (gobject_class,
 
622
         PROP_START_RECONCILED_BALANCE,
 
623
         g_param_spec_boxed("start-reconciled-balance",
 
624
                            "Starting Reconciled Balance",
 
625
                            "The starting reconciled balance for the "
 
626
                            "account.  This parameter is intended for use "
 
627
                            "with backends that do not return the complete "
 
628
                            "list of splits for an account, but rather return "
 
629
                            "a partial list.  In such a case, the backend "
 
630
                            "will typically return all of the splits after "
 
631
                            "some certain date, and the 'starting recontiled "
 
632
                            "balance' will represent the summation of the "
 
633
                            "splits up to that date.",
 
634
                             GNC_TYPE_NUMERIC,
 
635
                             G_PARAM_READWRITE));
 
636
 
 
637
    g_object_class_install_property
 
638
        (gobject_class,
 
639
         PROP_END_BALANCE,
 
640
         g_param_spec_boxed("end-balance",
 
641
                            "Ending Account Balance",
 
642
                            "This is the current ending balance for the "
 
643
                            "account.  It is computed from the sum of the "
 
644
                            "starting balance and all splits in the account.",
 
645
                            GNC_TYPE_NUMERIC,
 
646
                            G_PARAM_READABLE));
 
647
 
 
648
    g_object_class_install_property
 
649
        (gobject_class,
 
650
         PROP_END_CLEARED_BALANCE,
 
651
         g_param_spec_boxed("end-cleared-balance",
 
652
                            "Ending Account Cleared Balance",
 
653
                            "This is the current ending cleared balance for "
 
654
                            "the account.  It is computed from the sum of the "
 
655
                            "starting balance and all cleared splits in the "
 
656
                            "account.",
 
657
                            GNC_TYPE_NUMERIC,
 
658
                            G_PARAM_READABLE));
 
659
 
 
660
    g_object_class_install_property
 
661
        (gobject_class,
 
662
         PROP_END_RECONCILED_BALANCE,
 
663
         g_param_spec_boxed("end-reconciled-balance",
 
664
                            "Ending Account Reconciled Balance",
 
665
                            "This is the current ending reconciled balance "
 
666
                            "for the account.  It is computed from the sum of "
 
667
                            "the starting balance and all reconciled splits "
 
668
                            "in the account.",
 
669
                            GNC_TYPE_NUMERIC,
 
670
                            G_PARAM_READABLE));
 
671
 
 
672
    g_object_class_install_property
 
673
        (gobject_class,
 
674
         PROP_POLICY,
 
675
         g_param_spec_pointer ("policy",
 
676
                               "Policy",
 
677
                               "The account lots policy.",
 
678
                               G_PARAM_READWRITE));
 
679
 
 
680
    g_object_class_install_property
 
681
        (gobject_class,
 
682
         PROP_MARK,
 
683
         g_param_spec_int ("acct-mark",
 
684
                           "Account Mark",
 
685
                           "Ipsum Lorem",
 
686
                           0,
 
687
                           G_MAXINT16,
 
688
                           0,
 
689
                           G_PARAM_READWRITE));
 
690
 
 
691
    g_object_class_install_property
 
692
        (gobject_class,
 
693
         PROP_TAX_RELATED,
 
694
         g_param_spec_boolean ("tax-related",
 
695
                               "Tax Related",
 
696
                               "Whether the account maps to an entry on an "
 
697
                               "income tax document.",
 
698
                               FALSE,
 
699
                               G_PARAM_READWRITE));
 
700
 
 
701
    g_object_class_install_property
 
702
        (gobject_class,
 
703
         PROP_TAX_CODE,
 
704
         g_param_spec_string ("tax-code",
 
705
                              "Tax Code",
 
706
                              "This is the code for mapping an account to a "
 
707
                              "specific entry on a taxable document.  In the "
 
708
                              "United States it is used to transfer totals "
 
709
                              "into tax preparation software.",
 
710
                              NULL,
 
711
                              G_PARAM_READWRITE));
 
712
 
 
713
    g_object_class_install_property
 
714
        (gobject_class,
 
715
         PROP_TAX_SOURCE,
 
716
         g_param_spec_string ("tax-source",
 
717
                              "Tax Source",
 
718
                              "This is an unknown tax related field.",
 
719
                              NULL,
 
720
                              G_PARAM_READWRITE));
 
721
}
 
722
 
 
723
static void
 
724
xaccInitAccount (Account * acc, QofBook *book)
 
725
{
 
726
  ENTER ("book=%p\n", book);
 
727
  qof_instance_init_data (&acc->inst, GNC_ID_ACCOUNT, book);
 
728
 
 
729
  LEAVE ("account=%p\n", acc);
 
730
}
 
731
 
 
732
/********************************************************************\
 
733
\********************************************************************/
 
734
 
 
735
QofBook *
 
736
gnc_account_get_book(const Account *account)
 
737
{
 
738
  return qof_instance_get_book(QOF_INSTANCE(account));
 
739
}
 
740
 
 
741
/********************************************************************\
 
742
\********************************************************************/
 
743
 
 
744
static Account * 
 
745
gnc_coll_get_root_account (QofCollection *col)
 
746
{
 
747
  if (!col) return NULL;
 
748
  return qof_collection_get_data (col);
 
749
}
 
750
 
 
751
static void
 
752
gnc_coll_set_root_account (QofCollection *col, Account *root)
 
753
{
 
754
  AccountPrivate *rpriv;
 
755
  Account *old_root;
 
756
  if (!col) return;
 
757
 
 
758
  old_root = gnc_coll_get_root_account (col);
 
759
  if (old_root == root) return;
 
760
 
 
761
  /* If the new root is already linked into the tree somewhere, then
 
762
   * remove it from its current position before adding it at the
 
763
   * top. */
 
764
  rpriv = GET_PRIVATE(root);
 
765
  if (rpriv->parent) {
 
766
    xaccAccountBeginEdit(root);
 
767
    gnc_account_remove_child(rpriv->parent, root);
 
768
    xaccAccountCommitEdit(root);
 
769
  }
 
770
    
 
771
  qof_collection_set_data (col, root);
 
772
 
 
773
  if (old_root) {
 
774
    xaccAccountBeginEdit (old_root);
 
775
    xaccAccountDestroy (old_root);
 
776
  }
 
777
}
 
778
 
 
779
Account *
 
780
gnc_book_get_root_account (QofBook *book)
 
781
{
 
782
  QofCollection *col;
 
783
  Account *root;
 
784
 
 
785
  if (!book) return NULL;
 
786
  col = qof_book_get_collection (book, GNC_ID_ROOT_ACCOUNT);
 
787
  root = gnc_coll_get_root_account (col);
 
788
  if (root == NULL)
 
789
    root = gnc_account_create_root(book);
 
790
  return root;
 
791
}
 
792
 
 
793
void
 
794
gnc_book_set_root_account (QofBook *book, Account *root)
 
795
{
 
796
  QofCollection *col;
 
797
  if (!book) return;
 
798
 
 
799
  if (root && gnc_account_get_book(root) != book)
 
800
  {
 
801
     PERR ("cannot mix and match books freely!");
 
802
     return;
 
803
  }
 
804
 
 
805
  col = qof_book_get_collection (book, GNC_ID_ROOT_ACCOUNT);
 
806
  gnc_coll_set_root_account (col, root);
 
807
}
 
808
 
 
809
/********************************************************************\
 
810
\********************************************************************/
 
811
 
 
812
Account *
 
813
xaccMallocAccount (QofBook *book)
 
814
{
 
815
  Account *acc;
 
816
 
 
817
  g_return_val_if_fail (book, NULL);
 
818
 
 
819
  acc = g_object_new (GNC_TYPE_ACCOUNT, NULL);
 
820
  xaccInitAccount (acc, book);
 
821
  qof_event_gen (&acc->inst, QOF_EVENT_CREATE, NULL);
 
822
 
 
823
  return acc;
 
824
}
 
825
 
 
826
Account *
 
827
gnc_account_create_root (QofBook *book)
 
828
{
 
829
  Account *root;
 
830
  AccountPrivate *rpriv;
 
831
 
 
832
  root = xaccMallocAccount(book);
 
833
  rpriv = GET_PRIVATE(root);
 
834
  xaccAccountBeginEdit(root);
 
835
  rpriv->type = ACCT_TYPE_ROOT;
 
836
  CACHE_REPLACE(rpriv->accountName, "Root Account");
 
837
  xaccAccountCommitEdit(root);
 
838
  gnc_book_set_root_account(book, root);
 
839
  return root;
 
840
}
 
841
 
 
842
static Account *
 
843
xaccCloneAccountCommon(const Account *from, QofBook *book)
 
844
{
 
845
    Account *ret;
 
846
    AccountPrivate *from_priv, *priv;
 
847
 
 
848
    g_return_val_if_fail(GNC_IS_ACCOUNT(from), NULL);
 
849
    g_return_val_if_fail(QOF_IS_BOOK(book), NULL);
 
850
 
 
851
    ENTER (" ");
 
852
    ret = g_object_new (GNC_TYPE_ACCOUNT, NULL);
 
853
    g_return_val_if_fail (ret, NULL);
 
854
 
 
855
    from_priv = GET_PRIVATE(from);
 
856
    priv = GET_PRIVATE(ret);
 
857
    xaccInitAccount (ret, book);
 
858
 
 
859
    /* Do not Begin/CommitEdit() here; give the caller 
 
860
     * a chance to fix things up, and let them do it.
 
861
     * Also let caller issue the generate_event (EVENT_CREATE) */
 
862
    priv->type = from_priv->type;
 
863
 
 
864
    priv->accountName = CACHE_INSERT(from_priv->accountName);
 
865
    priv->accountCode = CACHE_INSERT(from_priv->accountCode);
 
866
    priv->description = CACHE_INSERT(from_priv->description);
 
867
 
 
868
    kvp_frame_delete(ret->inst.kvp_data);
 
869
    ret->inst.kvp_data = kvp_frame_copy(from->inst.kvp_data);
 
870
 
 
871
    /* The new book should contain a commodity that matches
 
872
     * the one in the old book. Find it, use it. */
 
873
    priv->commodity = gnc_commodity_obtain_twin(from_priv->commodity, book);
 
874
 
 
875
    priv->commodity_scu = from_priv->commodity_scu;
 
876
    priv->non_standard_scu = from_priv->non_standard_scu;
 
877
 
 
878
    LEAVE (" ");
 
879
    return ret;
 
880
}
 
881
 
 
882
Account *
 
883
xaccCloneAccount (const Account *from, QofBook *book)
 
884
{
 
885
    Account *ret = xaccCloneAccountCommon(from, book);
 
886
    qof_instance_gemini (&ret->inst, (QofInstance *) &from->inst);
 
887
    g_assert (ret ==
 
888
              (Account*) qof_instance_lookup_twin (QOF_INSTANCE(from), book));
 
889
    return ret;
 
890
}
 
891
 
 
892
Account *
 
893
xaccCloneAccountSimple (const Account *from, QofBook *book)
 
894
{
 
895
    Account *ret = xaccCloneAccountCommon(from, book);    
 
896
    qof_instance_set_dirty(&ret->inst);
 
897
    return ret;
 
898
}
 
899
 
 
900
/********************************************************************\
 
901
\********************************************************************/
 
902
 
 
903
static void
 
904
xaccFreeOneChildAccount (Account *acc, gpointer dummy)
 
905
{
 
906
    /* FIXME: this code is kind of hacky.  actually, all this code
 
907
     * seems to assume that the account edit levels are all 1. */
 
908
    if (qof_instance_get_editlevel(acc) == 0)
 
909
      xaccAccountBeginEdit(acc);
 
910
    xaccAccountDestroy(acc);
 
911
}
 
912
 
 
913
static void
 
914
xaccFreeAccountChildren (Account *acc)
 
915
{
 
916
  AccountPrivate *priv;
 
917
  GList *children;
 
918
 
 
919
  /* Copy the list since it will be modified */
 
920
  priv = GET_PRIVATE(acc);
 
921
  children = g_list_copy(priv->children);
 
922
  g_list_foreach(children, (GFunc)xaccFreeOneChildAccount, NULL);
 
923
  g_list_free(children);
 
924
 
 
925
  /* The foreach should have removed all the children already. */
 
926
  if (priv->children)
 
927
    g_list_free(priv->children);
 
928
  priv->children = NULL;
 
929
}
 
930
 
 
931
/* The xaccFreeAccount() routine releases memory associated with the
 
932
 * account.  It should never be called directly from user code;
 
933
 * instead, the xaccAccountDestroy() routine should be used (because
 
934
 * xaccAccountDestroy() has the correct commit semantics). */
 
935
static void
 
936
xaccFreeAccount (Account *acc)
 
937
{
 
938
  AccountPrivate *priv;
 
939
  GList *lp;
 
940
 
 
941
  g_return_if_fail(GNC_IS_ACCOUNT(acc));
 
942
 
 
943
  priv = GET_PRIVATE(acc);
 
944
  qof_event_gen (&acc->inst, QOF_EVENT_DESTROY, NULL);
 
945
 
 
946
  if (priv->children) 
 
947
  {
 
948
    PERR (" instead of calling xaccFreeAccount(), please call \n"
 
949
          " xaccAccountBeginEdit(); xaccAccountDestroy(); \n");
 
950
 
 
951
    /* First, recursively free children */
 
952
    xaccFreeAccountChildren(acc);
 
953
  }
 
954
 
 
955
  /* remove lots -- although these should be gone by now. */
 
956
  if (priv->lots)
 
957
  {
 
958
    PERR (" instead of calling xaccFreeAccount(), please call \n"
 
959
          " xaccAccountBeginEdit(); xaccAccountDestroy(); \n");
 
960
  
 
961
    for (lp=priv->lots; lp; lp=lp->next)
 
962
    {
 
963
      GNCLot *lot = lp->data;
 
964
      gnc_lot_destroy (lot);
 
965
    }
 
966
    g_list_free (priv->lots);
 
967
    priv->lots = NULL;
 
968
  }
 
969
 
 
970
  /* Next, clean up the splits */
 
971
  /* NB there shouldn't be any splits by now ... they should 
 
972
   * have been all been freed by CommitEdit().  We can remove this
 
973
   * check once we know the warning isn't occurring any more. */
 
974
  if (priv->splits) 
 
975
  {
 
976
    GList *slist;
 
977
    PERR (" instead of calling xaccFreeAccount(), please call \n"
 
978
          " xaccAccountBeginEdit(); xaccAccountDestroy(); \n");
 
979
  
 
980
    qof_instance_reset_editlevel(acc);
 
981
 
 
982
    slist = g_list_copy(priv->splits);
 
983
    for (lp = slist; lp; lp = lp->next) {
 
984
      Split *s = (Split *) lp->data;
 
985
      g_assert(xaccSplitGetAccount(s) == acc);
 
986
      xaccSplitDestroy (s);
 
987
    }
 
988
    g_list_free(slist);
 
989
    g_assert(priv->splits == NULL);
 
990
  }
 
991
 
 
992
  CACHE_REPLACE(priv->accountName, NULL);
 
993
  CACHE_REPLACE(priv->accountCode, NULL);
 
994
  CACHE_REPLACE(priv->description, NULL);
 
995
 
 
996
  /* zero out values, just in case stray 
 
997
   * pointers are pointing here. */
 
998
 
 
999
  priv->parent = NULL;
 
1000
  priv->children = NULL;
 
1001
 
 
1002
  priv->balance  = gnc_numeric_zero();
 
1003
  priv->cleared_balance = gnc_numeric_zero();
 
1004
  priv->reconciled_balance = gnc_numeric_zero();
 
1005
 
 
1006
  priv->type = ACCT_TYPE_NONE;
 
1007
  priv->commodity = NULL;
 
1008
 
 
1009
  priv->balance_dirty = FALSE;
 
1010
  priv->sort_dirty = FALSE;
 
1011
 
 
1012
  /* qof_instance_release (&acc->inst); */
 
1013
  g_object_unref(acc);
 
1014
}
 
1015
 
 
1016
/********************************************************************\
 
1017
 * transactional routines
 
1018
\********************************************************************/
 
1019
 
 
1020
void 
 
1021
xaccAccountBeginEdit (Account *acc) 
 
1022
{
 
1023
        g_return_if_fail(acc);
 
1024
        qof_begin_edit(&acc->inst);
 
1025
}
 
1026
 
 
1027
static void on_done(QofInstance *inst) 
 
1028
{
 
1029
    /* old event style */
 
1030
    qof_event_gen (inst, QOF_EVENT_MODIFY, NULL);
 
1031
}
 
1032
 
 
1033
static void on_err (QofInstance *inst, QofBackendError errcode)
 
1034
{
 
1035
  PERR("commit error: %d", errcode);
 
1036
}
 
1037
 
 
1038
static void acc_free (QofInstance *inst)
 
1039
{
 
1040
  AccountPrivate *priv;
 
1041
  Account *acc = (Account *) inst;
 
1042
 
 
1043
  priv = GET_PRIVATE(acc);
 
1044
  if (priv->parent)
 
1045
    gnc_account_remove_child(priv->parent, acc);
 
1046
  xaccFreeAccount(acc);
 
1047
}
 
1048
 
 
1049
static void
 
1050
destroy_pending_splits_for_account(QofInstance *ent, gpointer acc)
 
1051
{
 
1052
    Transaction *trans = (Transaction *) ent;
 
1053
    Split *split;
 
1054
 
 
1055
    if (xaccTransIsOpen(trans))
 
1056
        while ((split = xaccTransFindSplitByAccount(trans, acc)))
 
1057
            xaccSplitDestroy(split);
 
1058
}
 
1059
 
 
1060
void 
 
1061
xaccAccountCommitEdit (Account *acc) 
 
1062
{
 
1063
  AccountPrivate *priv;
 
1064
  QofBook *book;
 
1065
 
 
1066
  g_return_if_fail(acc);
 
1067
  if (!qof_commit_edit(&acc->inst)) return;
 
1068
 
 
1069
  /* If marked for deletion, get rid of subaccounts first,
 
1070
   * and then the splits ... */
 
1071
  priv = GET_PRIVATE(acc);
 
1072
  if (qof_instance_get_destroying(acc))
 
1073
  {
 
1074
    GList *lp, *slist;
 
1075
    QofCollection *col;
 
1076
 
 
1077
    qof_instance_increase_editlevel(acc);
 
1078
 
 
1079
    /* First, recursively free children */
 
1080
    xaccFreeAccountChildren(acc);
 
1081
 
 
1082
    PINFO ("freeing splits for account %p (%s)",
 
1083
           acc, priv->accountName ? priv->accountName : "(null)");
 
1084
 
 
1085
    slist = g_list_copy(priv->splits);
 
1086
    for (lp = slist; lp; lp = lp->next)
 
1087
    {
 
1088
      Split *s = lp->data;
 
1089
      xaccSplitDestroy (s);
 
1090
    }
 
1091
    g_list_free(slist); 
 
1092
    /* It turns out there's a case where this assertion does not hold:
 
1093
       When the user tries to delete an Imbalance account, while also
 
1094
       deleting all the splits in it.  The splits will just get
 
1095
       recreated and put right back into the same account!
 
1096
 
 
1097
       g_assert(priv->splits == NULL || qof_book_shutting_down(acc->inst.book));
 
1098
    */
 
1099
 
 
1100
    book = qof_instance_get_book(acc);
 
1101
    if (!qof_book_shutting_down(book)) {
 
1102
      col = qof_book_get_collection(book, GNC_ID_TRANS);
 
1103
      qof_collection_foreach(col, destroy_pending_splits_for_account, acc);
 
1104
    }
 
1105
 
 
1106
    /* the lots should be empty by now */
 
1107
    for (lp = priv->lots; lp; lp = lp->next)
 
1108
    {
 
1109
      GNCLot *lot = lp->data;
 
1110
      gnc_lot_destroy (lot);
 
1111
    }
 
1112
    g_list_free(priv->lots);
 
1113
    priv->lots = NULL;
 
1114
 
 
1115
    qof_instance_set_dirty(&acc->inst);
 
1116
    qof_instance_decrease_editlevel(acc);
 
1117
  }
 
1118
  else 
 
1119
  {
 
1120
    xaccAccountBringUpToDate(acc);
 
1121
  }
 
1122
 
 
1123
  qof_commit_edit_part2(&acc->inst, on_err, on_done, acc_free);
 
1124
}
 
1125
 
 
1126
void 
 
1127
xaccAccountDestroy (Account *acc) 
 
1128
{
 
1129
  g_return_if_fail(GNC_IS_ACCOUNT(acc));
 
1130
 
 
1131
  qof_instance_set_destroying(acc, TRUE);
 
1132
 
 
1133
  xaccAccountCommitEdit (acc);
 
1134
}
 
1135
 
 
1136
/********************************************************************\
 
1137
\********************************************************************/
 
1138
 
 
1139
static gboolean
 
1140
xaccAcctChildrenEqual(const GList *na,
 
1141
                      const GList *nb,
 
1142
                      gboolean check_guids)
 
1143
{
 
1144
  if ((!na && nb) || (na && !nb))
 
1145
  {
 
1146
    PWARN ("only one has accounts");
 
1147
    return(FALSE);
 
1148
  }
 
1149
 
 
1150
  while (na && nb)
 
1151
  {
 
1152
    Account *aa = na->data;
 
1153
    Account *ab = nb->data;
 
1154
 
 
1155
    if (!xaccAccountEqual(aa, ab, check_guids))
 
1156
    {
 
1157
      char sa[GUID_ENCODING_LENGTH + 1];
 
1158
      char sb[GUID_ENCODING_LENGTH + 1];
 
1159
 
 
1160
      guid_to_string_buff (xaccAccountGetGUID (aa), sa);
 
1161
      guid_to_string_buff (xaccAccountGetGUID (ab), sb);
 
1162
 
 
1163
      PWARN ("accounts %s and %s differ", sa, sb);
 
1164
 
 
1165
      return(FALSE);
 
1166
    }
 
1167
 
 
1168
    na = na->next;
 
1169
    nb = nb->next;
 
1170
  }
 
1171
 
 
1172
  if (na || nb)
 
1173
  {
 
1174
    PWARN ("different numbers of accounts");
 
1175
    return(FALSE);
 
1176
  }
 
1177
 
 
1178
  return(TRUE);
 
1179
}
 
1180
 
 
1181
gboolean
 
1182
xaccAccountEqual(const Account *aa, const Account *ab, gboolean check_guids)
 
1183
{
 
1184
  AccountPrivate *priv_aa, *priv_ab;
 
1185
 
 
1186
  if(!aa && !ab) return TRUE;
 
1187
 
 
1188
  g_return_val_if_fail(GNC_IS_ACCOUNT(aa), FALSE);
 
1189
  g_return_val_if_fail(GNC_IS_ACCOUNT(ab), FALSE);
 
1190
 
 
1191
  priv_aa = GET_PRIVATE(aa);
 
1192
  priv_ab = GET_PRIVATE(ab);
 
1193
  if (priv_aa->type != priv_ab->type)
 
1194
  {
 
1195
    PWARN ("types differ: %d vs %d", priv_aa->type, priv_ab->type);
 
1196
    return FALSE;
 
1197
  }
 
1198
 
 
1199
  if (safe_strcmp(priv_aa->accountName, priv_ab->accountName) != 0)
 
1200
  {
 
1201
    PWARN ("names differ: %s vs %s", priv_aa->accountName, priv_ab->accountName);
 
1202
    return FALSE;
 
1203
  }
 
1204
 
 
1205
  if (safe_strcmp(priv_aa->accountCode, priv_ab->accountCode) != 0)
 
1206
  {
 
1207
    PWARN ("codes differ: %s vs %s", priv_aa->accountCode, priv_ab->accountCode);
 
1208
    return FALSE;
 
1209
  }
 
1210
 
 
1211
  if (safe_strcmp(priv_aa->description, priv_ab->description) != 0)
 
1212
  {
 
1213
    PWARN ("descriptions differ: %s vs %s", priv_aa->description, priv_ab->description);
 
1214
    return FALSE;
 
1215
  }
 
1216
 
 
1217
  if (!gnc_commodity_equal(priv_aa->commodity, priv_ab->commodity))
 
1218
  {
 
1219
    PWARN ("commodities differ");
 
1220
    return FALSE;
 
1221
  }
 
1222
 
 
1223
  if(check_guids) {
 
1224
    if(qof_instance_guid_compare(aa, ab) != 0)
 
1225
    {
 
1226
      PWARN ("GUIDs differ");
 
1227
      return FALSE;
 
1228
    }
 
1229
  }
 
1230
 
 
1231
  if (kvp_frame_compare(aa->inst.kvp_data, ab->inst.kvp_data) != 0)
 
1232
  {
 
1233
    char *frame_a;
 
1234
    char *frame_b;
 
1235
 
 
1236
    frame_a = kvp_frame_to_string (aa->inst.kvp_data);
 
1237
    frame_b = kvp_frame_to_string (ab->inst.kvp_data);
 
1238
 
 
1239
    PWARN ("kvp frames differ:\n%s\n\nvs\n\n%s", frame_a, frame_b);
 
1240
 
 
1241
    g_free (frame_a);
 
1242
    g_free (frame_b);
 
1243
 
 
1244
    return FALSE;
 
1245
  }
 
1246
 
 
1247
  if (!gnc_numeric_equal(priv_aa->starting_balance, priv_ab->starting_balance))
 
1248
  {
 
1249
    char *str_a;
 
1250
    char *str_b;
 
1251
 
 
1252
    str_a = gnc_numeric_to_string(priv_aa->starting_balance);
 
1253
    str_b = gnc_numeric_to_string(priv_ab->starting_balance);
 
1254
 
 
1255
    PWARN ("starting balances differ: %s vs %s", str_a, str_b);
 
1256
 
 
1257
    g_free (str_a);
 
1258
    g_free (str_b);
 
1259
 
 
1260
    return FALSE;
 
1261
  }
 
1262
 
 
1263
  if (!gnc_numeric_equal(priv_aa->starting_cleared_balance,
 
1264
                         priv_ab->starting_cleared_balance))
 
1265
  {
 
1266
    char *str_a;
 
1267
    char *str_b;
 
1268
 
 
1269
    str_a = gnc_numeric_to_string(priv_aa->starting_cleared_balance);
 
1270
    str_b = gnc_numeric_to_string(priv_ab->starting_cleared_balance);
 
1271
 
 
1272
    PWARN ("starting cleared balances differ: %s vs %s", str_a, str_b);
 
1273
 
 
1274
    g_free (str_a);
 
1275
    g_free (str_b);
 
1276
 
 
1277
    return FALSE;
 
1278
  }
 
1279
 
 
1280
  if (!gnc_numeric_equal(priv_aa->starting_reconciled_balance,
 
1281
                         priv_ab->starting_reconciled_balance))
 
1282
  {
 
1283
    char *str_a;
 
1284
    char *str_b;
 
1285
 
 
1286
    str_a = gnc_numeric_to_string(priv_aa->starting_reconciled_balance);
 
1287
    str_b = gnc_numeric_to_string(priv_ab->starting_reconciled_balance);
 
1288
 
 
1289
    PWARN ("starting reconciled balances differ: %s vs %s", str_a, str_b);
 
1290
 
 
1291
    g_free (str_a);
 
1292
    g_free (str_b);
 
1293
 
 
1294
    return FALSE;
 
1295
  }
 
1296
 
 
1297
  if (!gnc_numeric_equal(priv_aa->balance, priv_ab->balance))
 
1298
  {
 
1299
    char *str_a;
 
1300
    char *str_b;
 
1301
 
 
1302
    str_a = gnc_numeric_to_string(priv_aa->balance);
 
1303
    str_b = gnc_numeric_to_string(priv_ab->balance);
 
1304
 
 
1305
    PWARN ("balances differ: %s vs %s", str_a, str_b);
 
1306
 
 
1307
    g_free (str_a);
 
1308
    g_free (str_b);
 
1309
 
 
1310
    return FALSE;
 
1311
  }
 
1312
 
 
1313
  if (!gnc_numeric_equal(priv_aa->cleared_balance, priv_ab->cleared_balance))
 
1314
  {
 
1315
    char *str_a;
 
1316
    char *str_b;
 
1317
 
 
1318
    str_a = gnc_numeric_to_string(priv_aa->cleared_balance);
 
1319
    str_b = gnc_numeric_to_string(priv_ab->cleared_balance);
 
1320
 
 
1321
    PWARN ("cleared balances differ: %s vs %s", str_a, str_b);
 
1322
 
 
1323
    g_free (str_a);
 
1324
    g_free (str_b);
 
1325
 
 
1326
    return FALSE;
 
1327
  }
 
1328
 
 
1329
  if (!gnc_numeric_equal(priv_aa->reconciled_balance, priv_ab->reconciled_balance))
 
1330
  {
 
1331
    char *str_a;
 
1332
    char *str_b;
 
1333
 
 
1334
    str_a = gnc_numeric_to_string(priv_aa->reconciled_balance);
 
1335
    str_b = gnc_numeric_to_string(priv_ab->reconciled_balance);
 
1336
 
 
1337
    PWARN ("reconciled balances differ: %s vs %s", str_a, str_b);
 
1338
 
 
1339
    g_free (str_a);
 
1340
    g_free (str_b);
 
1341
 
 
1342
    return FALSE;
 
1343
  }
 
1344
 
 
1345
  /* no parent; always compare downwards. */
 
1346
 
 
1347
  {
 
1348
    GList *la = priv_aa->splits;
 
1349
    GList *lb = priv_ab->splits;
 
1350
 
 
1351
    if ((la && !lb) || (!la && lb))
 
1352
    {
 
1353
      PWARN ("only one has splits");
 
1354
      return FALSE;
 
1355
    }
 
1356
 
 
1357
    if(la && lb)
 
1358
    {
 
1359
      /* presume that the splits are in the same order */
 
1360
      while (la && lb)
 
1361
      {
 
1362
        Split *sa = (Split *) la->data;
 
1363
        Split *sb = (Split *) lb->data;
 
1364
 
 
1365
        if (!xaccSplitEqual(sa, sb, check_guids, TRUE, FALSE))
 
1366
        {
 
1367
          PWARN ("splits differ");
 
1368
          return(FALSE);
 
1369
        }
 
1370
 
 
1371
        la = la->next;
 
1372
        lb = lb->next;
 
1373
      }
 
1374
 
 
1375
      if ((la != NULL) || (lb != NULL))
 
1376
      {
 
1377
        PWARN ("number of splits differs");
 
1378
        return(FALSE);
 
1379
      }
 
1380
    }
 
1381
  }
 
1382
 
 
1383
  if (!xaccAcctChildrenEqual(priv_aa->children, priv_ab->children, check_guids))
 
1384
  {
 
1385
    PWARN ("children differ");
 
1386
    return FALSE;
 
1387
  }
 
1388
 
 
1389
  return(TRUE);
 
1390
}
 
1391
 
 
1392
/********************************************************************\
 
1393
\********************************************************************/
 
1394
 
 
1395
gboolean
 
1396
gnc_account_get_sort_dirty (Account *acc)
 
1397
{
 
1398
    g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
 
1399
    return GET_PRIVATE(acc)->sort_dirty;
 
1400
}
 
1401
                            
 
1402
void
 
1403
gnc_account_set_sort_dirty (Account *acc)
 
1404
{
 
1405
    AccountPrivate *priv;
 
1406
 
 
1407
    g_return_if_fail(GNC_IS_ACCOUNT(acc));
 
1408
 
 
1409
    if (qof_instance_get_destroying(acc))
 
1410
        return;
 
1411
 
 
1412
    priv = GET_PRIVATE(acc);
 
1413
    priv->sort_dirty = TRUE;
 
1414
}
 
1415
 
 
1416
gboolean
 
1417
gnc_account_get_balance_dirty (Account *acc)
 
1418
{
 
1419
    g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
 
1420
    return GET_PRIVATE(acc)->balance_dirty;
 
1421
}
 
1422
 
 
1423
void
 
1424
gnc_account_set_balance_dirty (Account *acc)
 
1425
{
 
1426
    AccountPrivate *priv;
 
1427
 
 
1428
    g_return_if_fail(GNC_IS_ACCOUNT(acc));
 
1429
 
 
1430
    if (qof_instance_get_destroying(acc))
 
1431
        return;
 
1432
 
 
1433
    priv = GET_PRIVATE(acc);
 
1434
    priv->balance_dirty = TRUE;
 
1435
}
 
1436
 
 
1437
/********************************************************************\
 
1438
\********************************************************************/
 
1439
 
 
1440
gboolean
 
1441
gnc_account_find_split (Account *acc, Split *s)
 
1442
{
 
1443
    AccountPrivate *priv;
 
1444
    GList *node;
 
1445
 
 
1446
    g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
 
1447
    g_return_val_if_fail(GNC_IS_SPLIT(s), FALSE);
 
1448
 
 
1449
    priv = GET_PRIVATE(acc);
 
1450
    node = g_list_find(priv->splits, s);
 
1451
    return node ? TRUE : FALSE;
 
1452
}
 
1453
 
 
1454
gboolean
 
1455
gnc_account_insert_split (Account *acc, Split *s)
 
1456
{
 
1457
    AccountPrivate *priv;
 
1458
    GList *node;
 
1459
 
 
1460
    g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
 
1461
    g_return_val_if_fail(GNC_IS_SPLIT(s), FALSE);
 
1462
 
 
1463
    priv = GET_PRIVATE(acc);
 
1464
    node = g_list_find(priv->splits, s);
 
1465
    if (node)
 
1466
        return FALSE;
 
1467
 
 
1468
    if (qof_instance_get_editlevel(acc) == 0) {
 
1469
        priv->splits = g_list_insert_sorted(priv->splits, s,
 
1470
                                           (GCompareFunc)xaccSplitOrder);
 
1471
    } else {
 
1472
        priv->splits = g_list_prepend(priv->splits, s);
 
1473
        priv->sort_dirty = TRUE;
 
1474
    }
 
1475
 
 
1476
    //FIXME: find better event
 
1477
    qof_event_gen (&acc->inst, QOF_EVENT_MODIFY, NULL);
 
1478
    /* Also send an event based on the account */
 
1479
    qof_event_gen(&acc->inst, GNC_EVENT_ITEM_ADDED, s);
 
1480
 
 
1481
    priv->balance_dirty = TRUE;
 
1482
//  DRH: Should the below be added? It is present in the delete path.
 
1483
//  xaccAccountRecomputeBalance(acc);
 
1484
    return TRUE;
 
1485
}
 
1486
 
 
1487
gboolean
 
1488
gnc_account_remove_split (Account *acc, Split *s)
 
1489
{
 
1490
    AccountPrivate *priv;
 
1491
    GList *node;
 
1492
 
 
1493
    g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
 
1494
    g_return_val_if_fail(GNC_IS_SPLIT(s), FALSE);
 
1495
 
 
1496
    priv = GET_PRIVATE(acc);
 
1497
    node = g_list_find(priv->splits, s);
 
1498
    if (NULL == node)
 
1499
        return FALSE;
 
1500
 
 
1501
    priv->splits = g_list_delete_link(priv->splits, node);
 
1502
    //FIXME: find better event type
 
1503
    qof_event_gen(&acc->inst, QOF_EVENT_MODIFY, NULL);
 
1504
    // And send the account-based event, too
 
1505
    qof_event_gen(&acc->inst, GNC_EVENT_ITEM_REMOVED, s);
 
1506
 
 
1507
    priv->balance_dirty = TRUE;
 
1508
    xaccAccountRecomputeBalance(acc);
 
1509
    return TRUE;
 
1510
}
 
1511
 
 
1512
void
 
1513
xaccAccountSortSplits (Account *acc, gboolean force)
 
1514
{
 
1515
    AccountPrivate *priv;
 
1516
 
 
1517
    g_return_if_fail(GNC_IS_ACCOUNT(acc));
 
1518
 
 
1519
    priv = GET_PRIVATE(acc);
 
1520
    if (!priv->sort_dirty || (!force && qof_instance_get_editlevel(acc) > 0))
 
1521
        return;
 
1522
    priv->splits = g_list_sort(priv->splits, (GCompareFunc)xaccSplitOrder);
 
1523
    priv->sort_dirty = FALSE;
 
1524
    priv->balance_dirty = TRUE;
 
1525
}
 
1526
 
 
1527
static void
 
1528
xaccAccountBringUpToDate(Account *acc) 
 
1529
{
 
1530
  if (!acc) return;
 
1531
 
 
1532
  /* if a re-sort happens here, then everything will update, so the
 
1533
     cost basis and balance calls are no-ops */
 
1534
  xaccAccountSortSplits(acc, FALSE);
 
1535
  xaccAccountRecomputeBalance(acc);
 
1536
}
 
1537
 
 
1538
/********************************************************************\
 
1539
\********************************************************************/
 
1540
 
 
1541
void 
 
1542
xaccAccountSetGUID (Account *acc, const GUID *guid)
 
1543
{
 
1544
  g_return_if_fail(GNC_IS_ACCOUNT(acc));
 
1545
  g_return_if_fail(guid);
 
1546
 
 
1547
  /* XXX this looks fishy and weird to me ... */
 
1548
  PINFO("acct=%p", acc);
 
1549
  xaccAccountBeginEdit (acc);
 
1550
  qof_instance_set_guid (&acc->inst, guid);
 
1551
  qof_instance_set_dirty(&acc->inst);
 
1552
  xaccAccountCommitEdit (acc);
 
1553
}
 
1554
 
 
1555
/********************************************************************\
 
1556
\********************************************************************/
 
1557
 
 
1558
Account *
 
1559
xaccAccountLookup (const GUID *guid, QofBook *book)
 
1560
{
 
1561
  QofCollection *col;
 
1562
  if (!guid || !book) return NULL;
 
1563
  col = qof_book_get_collection (book, GNC_ID_ACCOUNT);
 
1564
  return (Account *) qof_collection_lookup_entity (col, guid);
 
1565
}
 
1566
 
 
1567
/********************************************************************\
 
1568
\********************************************************************/
 
1569
 
 
1570
short
 
1571
xaccAccountGetMark (const Account *acc)
 
1572
{
 
1573
    g_return_val_if_fail(GNC_IS_ACCOUNT(acc), 0);
 
1574
 
 
1575
    return GET_PRIVATE(acc)->mark;
 
1576
}
 
1577
 
 
1578
void
 
1579
xaccAccountSetMark (Account *acc, short m)
 
1580
{
 
1581
    AccountPrivate *priv;
 
1582
 
 
1583
    g_return_if_fail(GNC_IS_ACCOUNT(acc));
 
1584
 
 
1585
    priv = GET_PRIVATE(acc);
 
1586
    priv->mark = m;
 
1587
}
 
1588
 
 
1589
void
 
1590
xaccClearMark (Account *acc, short val)
 
1591
{
 
1592
    Account *root;
 
1593
 
 
1594
    g_return_if_fail(GNC_IS_ACCOUNT(acc));
 
1595
 
 
1596
    root = gnc_account_get_root(acc);
 
1597
    xaccClearMarkDown(root ? root : acc, val);
 
1598
}
 
1599
 
 
1600
void
 
1601
xaccClearMarkDown (Account *acc, short val)
 
1602
{
 
1603
    AccountPrivate *priv;
 
1604
    GList *node;
 
1605
 
 
1606
    g_return_if_fail(GNC_IS_ACCOUNT(acc));
 
1607
 
 
1608
    priv = GET_PRIVATE(acc);
 
1609
    priv->mark = val;
 
1610
    for (node = priv->children; node; node = node->next) {
 
1611
        xaccClearMarkDown(node->data, val);
 
1612
    }
 
1613
}
 
1614
 
 
1615
/********************************************************************\
 
1616
\********************************************************************/
 
1617
 
 
1618
GNCPolicy *
 
1619
gnc_account_get_policy (Account *acc)
 
1620
{
 
1621
    g_return_val_if_fail(GNC_IS_ACCOUNT(acc), NULL);
 
1622
 
 
1623
    return GET_PRIVATE(acc)->policy;
 
1624
}
 
1625
 
 
1626
void
 
1627
gnc_account_set_policy (Account *acc, GNCPolicy *policy)
 
1628
{
 
1629
    AccountPrivate *priv;
 
1630
 
 
1631
    g_return_if_fail(GNC_IS_ACCOUNT(acc));
 
1632
 
 
1633
    priv = GET_PRIVATE(acc);
 
1634
    priv->policy = policy ? policy : xaccGetFIFOPolicy();
 
1635
}
 
1636
 
 
1637
/********************************************************************\
 
1638
\********************************************************************/
 
1639
 
 
1640
void
 
1641
xaccAccountRemoveLot (Account *acc, GNCLot *lot)
 
1642
{
 
1643
    AccountPrivate *priv;
 
1644
 
 
1645
    g_return_if_fail(GNC_IS_ACCOUNT(acc));
 
1646
    g_return_if_fail(GNC_IS_LOT(lot));
 
1647
 
 
1648
    priv = GET_PRIVATE(acc);
 
1649
    g_return_if_fail(priv->lots);
 
1650
 
 
1651
    ENTER ("(acc=%p, lot=%p)", acc, lot);
 
1652
    priv->lots = g_list_remove(priv->lots, lot);
 
1653
    LEAVE ("(acc=%p, lot=%p)", acc, lot);
 
1654
}
 
1655
 
 
1656
void
 
1657
xaccAccountInsertLot (Account *acc, GNCLot *lot)
 
1658
{
 
1659
    AccountPrivate *priv, *opriv;
 
1660
   Account * old_acc = NULL;
 
1661
 
 
1662
   /* errors */
 
1663
   g_return_if_fail(GNC_IS_ACCOUNT(acc));
 
1664
   g_return_if_fail(GNC_IS_LOT(lot));
 
1665
 
 
1666
   /* optimizations */
 
1667
   if (lot->account == acc)
 
1668
       return;
 
1669
 
 
1670
   ENTER ("(acc=%p, lot=%p)", acc, lot);
 
1671
 
 
1672
   /* pull it out of the old account */
 
1673
   if (lot->account) {
 
1674
      old_acc = lot->account;
 
1675
      opriv = GET_PRIVATE(old_acc);
 
1676
      opriv->lots = g_list_remove(opriv->lots, lot);
 
1677
   }
 
1678
 
 
1679
   priv = GET_PRIVATE(acc);
 
1680
   priv->lots = g_list_prepend(priv->lots, lot);
 
1681
   lot->account = acc;
 
1682
 
 
1683
   /* Don't move the splits to the new account.  The caller will do this
 
1684
    * if appropriate, and doing it here will not work if we are being 
 
1685
    * called from gnc_book_close_period since xaccAccountInsertSplit
 
1686
    * will try to balance capital gains and things aren't ready for that. */
 
1687
 
 
1688
   LEAVE ("(acc=%p, lot=%p)", acc, lot);
 
1689
}
 
1690
 
 
1691
/********************************************************************\
 
1692
\********************************************************************/
 
1693
static void
 
1694
xaccPreSplitMove (Split *split, gpointer dummy)
 
1695
{
 
1696
  xaccTransBeginEdit (xaccSplitGetParent (split));
 
1697
}
 
1698
 
 
1699
static void
 
1700
xaccPostSplitMove (Split *split, Account *accto)
 
1701
{
 
1702
  Transaction *trans;
 
1703
 
 
1704
  xaccSplitSetAccount(split, accto);
 
1705
  xaccSplitSetAmount(split, split->amount);
 
1706
  trans = xaccSplitGetParent (split);
 
1707
  xaccTransCommitEdit (trans);
 
1708
}
 
1709
 
 
1710
void
 
1711
xaccAccountMoveAllSplits (Account *accfrom, Account *accto)
 
1712
{
 
1713
  AccountPrivate *from_priv, *to_priv;
 
1714
 
 
1715
  /* errors */
 
1716
  g_return_if_fail(GNC_IS_ACCOUNT(accfrom));
 
1717
  g_return_if_fail(GNC_IS_ACCOUNT(accto));
 
1718
 
 
1719
  /* optimizations */
 
1720
  from_priv = GET_PRIVATE(accfrom);
 
1721
  to_priv = GET_PRIVATE(accto);
 
1722
  if (!from_priv->splits || accfrom == accto)
 
1723
      return;
 
1724
 
 
1725
  /* check for book mix-up */
 
1726
  g_return_if_fail (qof_instance_books_equal(accfrom, accto));
 
1727
  ENTER ("(accfrom=%p, accto=%p)", accfrom, accto);
 
1728
 
 
1729
  xaccAccountBeginEdit(accfrom);
 
1730
  xaccAccountBeginEdit(accto);
 
1731
  /* Begin editing both accounts and all transactions in accfrom. */
 
1732
  g_list_foreach(from_priv->splits, (GFunc)xaccPreSplitMove, NULL);
 
1733
 
 
1734
  /* Concatenate accfrom's lists of splits and lots to accto's lists. */
 
1735
  //to_priv->splits = g_list_concat(to_priv->splits, from_priv->splits);
 
1736
  //to_priv->lots = g_list_concat(to_priv->lots, from_priv->lots);
 
1737
 
 
1738
  /* Set appropriate flags. */
 
1739
  //from_priv->balance_dirty = TRUE;
 
1740
  //from_priv->sort_dirty = FALSE;
 
1741
  //to_priv->balance_dirty = TRUE;
 
1742
  //to_priv->sort_dirty = TRUE;
 
1743
 
 
1744
  /*
 
1745
   * Change each split's account back pointer to accto.
 
1746
   * Convert each split's amount to accto's commodity.
 
1747
   * Commit to editing each transaction.
 
1748
   */
 
1749
  g_list_foreach(from_priv->splits, (GFunc)xaccPostSplitMove, (gpointer)accto);
 
1750
 
 
1751
  /* Finally empty accfrom. */
 
1752
  g_assert(from_priv->splits == NULL);
 
1753
  g_assert(from_priv->lots == NULL);
 
1754
  xaccAccountCommitEdit(accfrom);
 
1755
  xaccAccountCommitEdit(accto);
 
1756
 
 
1757
  LEAVE ("(accfrom=%p, accto=%p)", accfrom, accto);
 
1758
}
 
1759
 
 
1760
 
 
1761
/********************************************************************\
 
1762
 * xaccAccountRecomputeBalance                                      *
 
1763
 *   recomputes the partial balances and the current balance for    *
 
1764
 *   this account.                                                  *
 
1765
 *                                                                  *
 
1766
 * The way the computation is done depends on whether the partial   *
 
1767
 * balances are for a monetary account (bank, cash, etc.) or a      *
 
1768
 * certificate account (stock portfolio, mutual fund).  For bank    *
 
1769
 * accounts, the invariant amount is the dollar amount. For share   *
 
1770
 * accounts, the invariant amount is the number of shares. For      *
 
1771
 * share accounts, the share price fluctuates, and the current      *
 
1772
 * value of such an account is the number of shares times the       *
 
1773
 * current share price.                                             *
 
1774
 *                                                                  *
 
1775
 * Part of the complexity of this computation stems from the fact   *
 
1776
 * xacc uses a double-entry system, meaning that one transaction    *
 
1777
 * appears in two accounts: one account is debited, and the other   *
 
1778
 * is credited.  When the transaction represents a sale of shares,  *
 
1779
 * or a purchase of shares, some care must be taken to compute      *
 
1780
 * balances correctly.  For a sale of shares, the stock account must*
 
1781
 * be debited in shares, but the bank account must be credited      *
 
1782
 * in dollars.  Thus, two different mechanisms must be used to      *
 
1783
 * compute balances, depending on account type.                     *
 
1784
 *                                                                  *
 
1785
 * Args:   account -- the account for which to recompute balances   *
 
1786
 * Return: void                                                     *
 
1787
\********************************************************************/
 
1788
 
 
1789
void
 
1790
xaccAccountRecomputeBalance (Account * acc)
 
1791
{
 
1792
  AccountPrivate *priv;
 
1793
  gnc_numeric  balance;
 
1794
  gnc_numeric  cleared_balance; 
 
1795
  gnc_numeric  reconciled_balance;
 
1796
  Split *last_split = NULL;
 
1797
  GList *lp;
 
1798
 
 
1799
  if (NULL == acc) return;
 
1800
 
 
1801
  priv = GET_PRIVATE(acc);
 
1802
  if (qof_instance_get_editlevel(acc) > 0) return;
 
1803
  if (!priv->balance_dirty) return;
 
1804
  if (qof_instance_get_destroying(acc)) return;
 
1805
  if (qof_book_shutting_down(qof_instance_get_book(acc))) return;
 
1806
 
 
1807
  balance            = priv->starting_balance;
 
1808
  cleared_balance    = priv->starting_cleared_balance;
 
1809
  reconciled_balance = priv->starting_reconciled_balance;
 
1810
 
 
1811
  PINFO ("acct=%s starting baln=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT,
 
1812
         priv->accountName, balance.num, balance.denom);
 
1813
  for(lp = priv->splits; lp; lp = lp->next) 
 
1814
  {
 
1815
    Split *split = (Split *) lp->data;
 
1816
    gnc_numeric amt = xaccSplitGetAmount (split);
 
1817
 
 
1818
    balance = gnc_numeric_add_fixed(balance, amt);
 
1819
 
 
1820
    if (NREC != split->reconciled)
 
1821
    {
 
1822
      cleared_balance = gnc_numeric_add_fixed(cleared_balance, amt);
 
1823
    }
 
1824
 
 
1825
    if (YREC == split->reconciled ||
 
1826
        FREC == split->reconciled) 
 
1827
    {
 
1828
      reconciled_balance =
 
1829
        gnc_numeric_add_fixed(reconciled_balance, amt);
 
1830
    }
 
1831
 
 
1832
    split->balance = balance;
 
1833
    split->cleared_balance = cleared_balance;
 
1834
    split->reconciled_balance = reconciled_balance;
 
1835
 
 
1836
    last_split = split;
 
1837
  }
 
1838
 
 
1839
  priv->balance = balance;
 
1840
  priv->cleared_balance = cleared_balance;
 
1841
  priv->reconciled_balance = reconciled_balance;
 
1842
  priv->balance_dirty = FALSE;
 
1843
}
 
1844
 
 
1845
/********************************************************************\
 
1846
\********************************************************************/
 
1847
 
 
1848
/* The sort order is used to implicitly define an 
 
1849
 * order for report generation */
 
1850
 
 
1851
static int typeorder[NUM_ACCOUNT_TYPES] = {
 
1852
     ACCT_TYPE_BANK, ACCT_TYPE_STOCK, ACCT_TYPE_MUTUAL, ACCT_TYPE_CURRENCY,
 
1853
     ACCT_TYPE_CASH, ACCT_TYPE_ASSET, ACCT_TYPE_RECEIVABLE,
 
1854
     ACCT_TYPE_CREDIT, ACCT_TYPE_LIABILITY, ACCT_TYPE_PAYABLE,
 
1855
     ACCT_TYPE_INCOME, ACCT_TYPE_EXPENSE, ACCT_TYPE_EQUITY };
 
1856
 
 
1857
static int revorder[NUM_ACCOUNT_TYPES] = {
 
1858
     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
 
1859
 
 
1860
 
 
1861
int
 
1862
xaccAccountOrder (const Account *aa, const Account *ab) 
 
1863
{
 
1864
  AccountPrivate *priv_aa, *priv_ab;
 
1865
  char *da, *db;
 
1866
  char *endptr = NULL;
 
1867
  int ta, tb, result;
 
1868
  long la, lb;
 
1869
 
 
1870
  if ( aa && !ab ) return -1;
 
1871
  if ( !aa && ab ) return +1;
 
1872
  if ( !aa && !ab ) return 0;
 
1873
 
 
1874
  priv_aa = GET_PRIVATE(aa);
 
1875
  priv_ab = GET_PRIVATE(ab);
 
1876
 
 
1877
  /* sort on accountCode strings */
 
1878
  da = priv_aa->accountCode;
 
1879
  db = priv_ab->accountCode;
 
1880
 
 
1881
  /* If accountCodes are both base 36 integers do an integer sort */
 
1882
  la = strtoul (da, &endptr, 36);
 
1883
  if((*da != '\0') && (*endptr == '\0')) {
 
1884
    lb = strtoul (db, &endptr, 36);
 
1885
    if((*db != '\0') && (*endptr == '\0')) {
 
1886
      if (la < lb) return -1;
 
1887
      if (la > lb) return +1;
 
1888
    }
 
1889
  }
 
1890
 
 
1891
  /* Otherwise do a string sort */
 
1892
  result = safe_strcmp (da, db);
 
1893
  if (result)
 
1894
    return result;
 
1895
 
 
1896
  /* if acccount-type-order array not initialized, initialize it */
 
1897
  /* this will happen at most once during program invocation */
 
1898
  if (-1 == revorder[0]) {
 
1899
    int i;
 
1900
    for (i=0; i<NUM_ACCOUNT_TYPES; i++) {
 
1901
      revorder [typeorder[i]] = i;
 
1902
    }
 
1903
  }
 
1904
 
 
1905
  /* otherwise, sort on account type */
 
1906
  ta = priv_aa->type;
 
1907
  tb = priv_ab->type;
 
1908
  ta = revorder[ta];
 
1909
  tb = revorder[tb];
 
1910
  if (ta < tb) return -1;
 
1911
  if (ta > tb) return +1;
 
1912
 
 
1913
  /* otherwise, sort on accountName strings */
 
1914
  da = priv_aa->accountName;
 
1915
  db = priv_ab->accountName;
 
1916
  result = safe_utf8_collate(da, db);
 
1917
  if (result)
 
1918
    return result;
 
1919
 
 
1920
  /* guarantee a stable sort */
 
1921
  return qof_instance_guid_compare(aa, ab);
 
1922
}
 
1923
 
 
1924
static int
 
1925
qof_xaccAccountOrder (const Account **aa, const Account **ab)
 
1926
{
 
1927
  return xaccAccountOrder(*aa, *ab);
 
1928
}
 
1929
 
 
1930
/********************************************************************\
 
1931
\********************************************************************/
 
1932
 
 
1933
void 
 
1934
xaccAccountSetType (Account *acc, GNCAccountType tip) 
 
1935
{
 
1936
  AccountPrivate *priv;
 
1937
 
 
1938
  /* errors */
 
1939
  g_return_if_fail(GNC_IS_ACCOUNT(acc));
 
1940
  g_return_if_fail(tip < NUM_ACCOUNT_TYPES);
 
1941
 
 
1942
  /* optimizations */
 
1943
  priv = GET_PRIVATE(acc);
 
1944
  if (priv->type == tip)
 
1945
      return;
 
1946
 
 
1947
  xaccAccountBeginEdit(acc);
 
1948
  priv->type = tip;
 
1949
  priv->balance_dirty = TRUE; /* new type may affect balance computation */
 
1950
  mark_account(acc);
 
1951
  xaccAccountCommitEdit(acc);
 
1952
}
 
1953
 
 
1954
void 
 
1955
xaccAccountSetName (Account *acc, const char *str) 
 
1956
{
 
1957
    AccountPrivate *priv;
 
1958
 
 
1959
    /* errors */
 
1960
    g_return_if_fail(GNC_IS_ACCOUNT(acc));
 
1961
    g_return_if_fail(str);
 
1962
 
 
1963
    /* optimizations */
 
1964
    priv = GET_PRIVATE(acc);
 
1965
    if (str == priv->accountName)
 
1966
        return;
 
1967
 
 
1968
    xaccAccountBeginEdit(acc);
 
1969
    CACHE_REPLACE(priv->accountName, str);
 
1970
    mark_account (acc);
 
1971
    xaccAccountCommitEdit(acc);
 
1972
}
 
1973
 
 
1974
void 
 
1975
xaccAccountSetCode (Account *acc, const char *str) 
 
1976
{
 
1977
    AccountPrivate *priv;
 
1978
 
 
1979
    /* errors */
 
1980
    g_return_if_fail(GNC_IS_ACCOUNT(acc));
 
1981
 
 
1982
    /* optimizations */
 
1983
    priv = GET_PRIVATE(acc);
 
1984
    if (str == priv->accountCode)
 
1985
        return;
 
1986
 
 
1987
    xaccAccountBeginEdit(acc);
 
1988
    CACHE_REPLACE(priv->accountCode, str ? str : "");
 
1989
    mark_account (acc);
 
1990
    xaccAccountCommitEdit(acc);
 
1991
}
 
1992
 
 
1993
void
 
1994
xaccAccountSetDescription (Account *acc, const char *str) 
 
1995
{
 
1996
    AccountPrivate *priv;
 
1997
 
 
1998
    /* errors */
 
1999
    g_return_if_fail(GNC_IS_ACCOUNT(acc));
 
2000
 
 
2001
    /* optimizations */
 
2002
    priv = GET_PRIVATE(acc);
 
2003
    if (str == priv->description)
 
2004
        return;
 
2005
 
 
2006
    xaccAccountBeginEdit(acc);
 
2007
    CACHE_REPLACE(priv->description, str ? str : "");
 
2008
    mark_account (acc);
 
2009
    xaccAccountCommitEdit(acc);
 
2010
}
 
2011
 
 
2012
static void
 
2013
qofAccountSetParent (Account *acc, QofInstance *parent) 
 
2014
{
 
2015
        Account *parent_acc;
 
2016
        
 
2017
        g_return_if_fail(GNC_IS_ACCOUNT(acc));
 
2018
        g_return_if_fail(GNC_IS_ACCOUNT(parent));
 
2019
 
 
2020
        parent_acc = GNC_ACCOUNT(parent);
 
2021
        xaccAccountBeginEdit(acc);
 
2022
        xaccAccountBeginEdit(parent_acc);
 
2023
        gnc_account_append_child(parent_acc, acc);
 
2024
        mark_account (parent_acc);
 
2025
        mark_account (acc);
 
2026
        xaccAccountCommitEdit(acc);
 
2027
        xaccAccountCommitEdit(parent_acc);
 
2028
}
 
2029
 
 
2030
void
 
2031
xaccAccountSetNotes (Account *acc, const char *str) 
 
2032
{
 
2033
  g_return_if_fail(GNC_IS_ACCOUNT(acc));
 
2034
 
 
2035
  xaccAccountBeginEdit(acc);
 
2036
  if (str) {
 
2037
    gchar *tmp = g_strstrip(g_strdup(str));
 
2038
    kvp_frame_set_slot_nc(acc->inst.kvp_data, "notes", 
 
2039
                          strlen(tmp) ? kvp_value_new_string(tmp) : NULL);
 
2040
    g_free(tmp);
 
2041
  } else {
 
2042
    kvp_frame_set_slot_nc(acc->inst.kvp_data, "notes", NULL);
 
2043
  }
 
2044
  mark_account(acc);
 
2045
  xaccAccountCommitEdit(acc);
 
2046
}
 
2047
 
 
2048
void 
 
2049
xaccAccountSetCommodity (Account * acc, gnc_commodity * com) 
 
2050
{
 
2051
  AccountPrivate *priv;
 
2052
  GList *lp;
 
2053
 
 
2054
  /* errors */
 
2055
  g_return_if_fail(GNC_IS_ACCOUNT(acc));
 
2056
  g_return_if_fail(GNC_IS_COMMODITY(com));
 
2057
 
 
2058
  /* optimizations */
 
2059
  priv = GET_PRIVATE(acc);
 
2060
  if (com == priv->commodity)
 
2061
      return;
 
2062
 
 
2063
  xaccAccountBeginEdit(acc);
 
2064
  priv->commodity = com;
 
2065
  priv->commodity_scu = gnc_commodity_get_fraction(com);
 
2066
  priv->non_standard_scu = FALSE;
 
2067
 
 
2068
  /* iterate over splits */
 
2069
  for (lp = priv->splits; lp; lp = lp->next)
 
2070
  {
 
2071
      Split *s = (Split *) lp->data;
 
2072
      Transaction *trans = xaccSplitGetParent (s);
 
2073
 
 
2074
      xaccTransBeginEdit (trans);
 
2075
      xaccSplitSetAmount (s, xaccSplitGetAmount(s));
 
2076
      xaccTransCommitEdit (trans);
 
2077
  }
 
2078
 
 
2079
  priv->sort_dirty = TRUE;  /* Not needed. */
 
2080
  priv->balance_dirty = TRUE;
 
2081
  mark_account (acc);
 
2082
 
 
2083
  if (gnc_commodity_is_iso(com)) {
 
2084
    /* compatability hack - Gnucash 1.8 gets currency quotes when a
 
2085
       non-default currency is assigned to an account.  */
 
2086
        gnc_commodity_begin_edit(com);
 
2087
    gnc_commodity_set_quote_flag(com, TRUE);
 
2088
    gnc_commodity_set_quote_source(com, 
 
2089
        gnc_commodity_get_default_quote_source(com));
 
2090
        gnc_commodity_commit_edit(com);
 
2091
  }
 
2092
  xaccAccountCommitEdit(acc);
 
2093
}
 
2094
 
 
2095
/*
 
2096
 * Set the account scu and then check to see if it is the same as the
 
2097
 * commodity scu.  This function is called when parsing the data file
 
2098
 * and is designed to catch cases where the two were accidentally set
 
2099
 * to mismatched values in the past.
 
2100
 */
 
2101
void
 
2102
xaccAccountSetCommoditySCU (Account *acc, int scu)
 
2103
{
 
2104
  AccountPrivate *priv;
 
2105
 
 
2106
  g_return_if_fail(GNC_IS_ACCOUNT(acc));
 
2107
 
 
2108
  priv = GET_PRIVATE(acc);
 
2109
  xaccAccountBeginEdit(acc);
 
2110
  priv->commodity_scu = scu;
 
2111
  if (scu != gnc_commodity_get_fraction(priv->commodity))
 
2112
      priv->non_standard_scu = TRUE;
 
2113
  mark_account(acc);
 
2114
  xaccAccountCommitEdit(acc);
 
2115
}
 
2116
 
 
2117
int
 
2118
xaccAccountGetCommoditySCUi (const Account * acc)
 
2119
{
 
2120
    g_return_val_if_fail(GNC_IS_ACCOUNT(acc), 0);
 
2121
    return GET_PRIVATE(acc)->commodity_scu;
 
2122
}
 
2123
 
 
2124
int
 
2125
xaccAccountGetCommoditySCU (const Account * acc)
 
2126
{
 
2127
    AccountPrivate *priv;
 
2128
 
 
2129
    g_return_val_if_fail(GNC_IS_ACCOUNT(acc), 0);
 
2130
 
 
2131
    priv = GET_PRIVATE(acc);
 
2132
    if (priv->non_standard_scu || !priv->commodity)
 
2133
        return priv->commodity_scu;
 
2134
    return gnc_commodity_get_fraction(priv->commodity);
 
2135
}
 
2136
 
 
2137
void
 
2138
xaccAccountSetNonStdSCU (Account *acc, gboolean flag)
 
2139
{
 
2140
  AccountPrivate *priv;
 
2141
 
 
2142
  g_return_if_fail(GNC_IS_ACCOUNT(acc));
 
2143
 
 
2144
  priv = GET_PRIVATE(acc);
 
2145
  if (priv->non_standard_scu == flag)
 
2146
      return;
 
2147
  xaccAccountBeginEdit(acc);
 
2148
  priv->non_standard_scu = flag;
 
2149
  mark_account (acc);
 
2150
  xaccAccountCommitEdit(acc);
 
2151
}
 
2152
 
 
2153
gboolean
 
2154
xaccAccountGetNonStdSCU (const Account * acc)
 
2155
{
 
2156
    g_return_val_if_fail(GNC_IS_ACCOUNT(acc), 0);
 
2157
    return GET_PRIVATE(acc)->non_standard_scu;
 
2158
}
 
2159
 
 
2160
/********************************************************************\
 
2161
\********************************************************************/
 
2162
/* below follow the old, deprecated currency/security routines. */
 
2163
 
 
2164
void 
 
2165
DxaccAccountSetCurrency (Account * acc, gnc_commodity * currency)
 
2166
{
 
2167
  QofBook *book;
 
2168
  const char *string;
 
2169
  gnc_commodity *commodity;
 
2170
 
 
2171
  if ((!acc) || (!currency)) return;
 
2172
 
 
2173
  xaccAccountBeginEdit(acc);
 
2174
  string = gnc_commodity_get_unique_name (currency);
 
2175
  kvp_frame_set_slot_nc(acc->inst.kvp_data, "old-currency",
 
2176
                        kvp_value_new_string(string));
 
2177
  mark_account (acc);
 
2178
  xaccAccountCommitEdit(acc);
 
2179
 
 
2180
  commodity = DxaccAccountGetCurrency (acc);
 
2181
  if (!commodity)
 
2182
  {
 
2183
    book = qof_instance_get_book(acc);
 
2184
    gnc_commodity_table_insert (gnc_commodity_table_get_table (book), currency);
 
2185
  }
 
2186
}
 
2187
 
 
2188
/********************************************************************\
 
2189
\********************************************************************/
 
2190
 
 
2191
void
 
2192
gnc_account_append_child (Account *new_parent, Account *child)
 
2193
{
 
2194
  AccountPrivate *ppriv, *cpriv;
 
2195
  Account *old_parent;
 
2196
  QofCollection *col;
 
2197
 
 
2198
  /* errors */
 
2199
  g_assert(GNC_IS_ACCOUNT(new_parent));
 
2200
  g_assert(GNC_IS_ACCOUNT(child));
 
2201
 
 
2202
  /* optimizations */
 
2203
  ppriv = GET_PRIVATE(new_parent);
 
2204
  cpriv = GET_PRIVATE(child);
 
2205
  old_parent = cpriv->parent;
 
2206
  if (old_parent == new_parent)
 
2207
    return;
 
2208
 
 
2209
  //  xaccAccountBeginEdit(new_parent);
 
2210
  xaccAccountBeginEdit(child);
 
2211
  if (old_parent) {
 
2212
    gnc_account_remove_child(old_parent, child);
 
2213
 
 
2214
    if (!qof_instance_books_equal(old_parent, new_parent)) {
 
2215
      /* hack alert -- this implementation is not exactly correct.
 
2216
       * If the entity tables are not identical, then the 'from' book 
 
2217
       * may have a different backend than the 'to' book.  This means
 
2218
       * that we should get the 'from' backend to destroy this account,
 
2219
       * and the 'to' backend to save it.  Right now, this is broken.
 
2220
       *  
 
2221
       * A 'correct' implementation similar to this is in Period.c
 
2222
       * except its for transactions ...
 
2223
       *
 
2224
       * Note also, we need to reparent the children to the new book as well.
 
2225
       */
 
2226
      PWARN ("reparenting accounts across books is not correctly supported\n");
 
2227
 
 
2228
      qof_event_gen (&child->inst, QOF_EVENT_DESTROY, NULL);
 
2229
      col = qof_book_get_collection (qof_instance_get_book(new_parent),
 
2230
                                     GNC_ID_ACCOUNT);
 
2231
      qof_collection_insert_entity (col, &child->inst);
 
2232
      qof_event_gen (&child->inst, QOF_EVENT_CREATE, NULL);
 
2233
    }
 
2234
  }
 
2235
  cpriv->parent = new_parent;
 
2236
  ppriv->children = g_list_append(ppriv->children, child);
 
2237
  qof_instance_set_dirty(&new_parent->inst);
 
2238
  qof_instance_set_dirty(&child->inst);
 
2239
 
 
2240
  /* Send events data. Warning: The call to commit_edit is also gpoing
 
2241
   * to send a MODIFY event. If the gtktreemodelfilter code gets the
 
2242
   * MODIFY before it gets the ADD, it gets very confused and thinks
 
2243
   * that two nodes have been added. */
 
2244
  qof_event_gen (&child->inst, QOF_EVENT_ADD, NULL);
 
2245
  // qof_event_gen (&new_parent->inst, QOF_EVENT_MODIFY, NULL);
 
2246
 
 
2247
  xaccAccountCommitEdit (child);
 
2248
  //  xaccAccountCommitEdit(new_parent);
 
2249
}
 
2250
 
 
2251
void
 
2252
gnc_account_remove_child (Account *parent, Account *child)
 
2253
{
 
2254
  AccountPrivate *ppriv, *cpriv;
 
2255
  GncEventData ed;
 
2256
 
 
2257
  if (!child) return;
 
2258
 
 
2259
  /* Note this routine might be called on accounts which 
 
2260
   * are not yet parented. */
 
2261
  if (!parent) return;
 
2262
 
 
2263
  ppriv = GET_PRIVATE(parent);
 
2264
  cpriv = GET_PRIVATE(child);
 
2265
 
 
2266
  if (cpriv->parent != parent)
 
2267
  {
 
2268
    PERR ("account not a child of parent");
 
2269
    return;
 
2270
  }
 
2271
 
 
2272
  /* Gather event data */
 
2273
  ed.node = parent;
 
2274
  ed.idx = g_list_index(ppriv->children, child);
 
2275
 
 
2276
  ppriv->children = g_list_remove(ppriv->children, child);
 
2277
 
 
2278
  /* Now send the event. */
 
2279
  qof_event_gen(&child->inst, QOF_EVENT_REMOVE, &ed);
 
2280
 
 
2281
  /* clear the account's parent pointer after REMOVE event generation. */
 
2282
  cpriv->parent = NULL;
 
2283
 
 
2284
  qof_event_gen (&parent->inst, QOF_EVENT_MODIFY, NULL);
 
2285
}
 
2286
 
 
2287
Account *
 
2288
gnc_account_get_parent (const Account *acc)
 
2289
{
 
2290
    g_return_val_if_fail(GNC_IS_ACCOUNT(acc), NULL);
 
2291
    return GET_PRIVATE(acc)->parent;
 
2292
}
 
2293
 
 
2294
Account *
 
2295
gnc_account_get_root (Account *acc)
 
2296
{
 
2297
    AccountPrivate *priv;
 
2298
 
 
2299
    g_return_val_if_fail(GNC_IS_ACCOUNT(acc), NULL);
 
2300
 
 
2301
    priv = GET_PRIVATE(acc);
 
2302
    while (priv->parent) {
 
2303
        acc = priv->parent;
 
2304
        priv = GET_PRIVATE(acc);
 
2305
    }
 
2306
 
 
2307
  return acc;
 
2308
}
 
2309
 
 
2310
gboolean
 
2311
gnc_account_is_root (const Account *account)
 
2312
{
 
2313
    g_return_val_if_fail(GNC_IS_ACCOUNT(account), FALSE);
 
2314
    return (GET_PRIVATE(account)->parent == NULL);
 
2315
}
 
2316
 
 
2317
GList *
 
2318
gnc_account_get_children (const Account *account)
 
2319
{
 
2320
    g_return_val_if_fail(GNC_IS_ACCOUNT(account), NULL);
 
2321
    return g_list_copy(GET_PRIVATE(account)->children);
 
2322
}
 
2323
 
 
2324
GList *
 
2325
gnc_account_get_children_sorted (const Account *account)
 
2326
{
 
2327
    AccountPrivate *priv;
 
2328
 
 
2329
    /* errors */
 
2330
    g_return_val_if_fail(GNC_IS_ACCOUNT(account), NULL);
 
2331
 
 
2332
    /* optimizations */
 
2333
    priv = GET_PRIVATE(account);
 
2334
    if (!priv->children)
 
2335
        return NULL;
 
2336
    return g_list_sort(g_list_copy(priv->children), (GCompareFunc)xaccAccountOrder);
 
2337
}
 
2338
 
 
2339
gint
 
2340
gnc_account_n_children (const Account *account)
 
2341
{
 
2342
    g_return_val_if_fail(GNC_IS_ACCOUNT(account), 0);
 
2343
    return g_list_length(GET_PRIVATE(account)->children);
 
2344
}
 
2345
 
 
2346
gint
 
2347
gnc_account_child_index (const Account *parent, const Account *child)
 
2348
{
 
2349
    g_return_val_if_fail(GNC_IS_ACCOUNT(parent), -1);
 
2350
    g_return_val_if_fail(GNC_IS_ACCOUNT(child), -1);
 
2351
    return g_list_index(GET_PRIVATE(parent)->children, child);
 
2352
}
 
2353
 
 
2354
Account *
 
2355
gnc_account_nth_child (const Account *parent, gint num)
 
2356
{
 
2357
    g_return_val_if_fail(GNC_IS_ACCOUNT(parent), NULL);
 
2358
    return g_list_nth_data(GET_PRIVATE(parent)->children, num);
 
2359
}
 
2360
 
 
2361
gint
 
2362
gnc_account_n_descendants (const Account *account)
 
2363
{
 
2364
    AccountPrivate *priv;
 
2365
    GList *node;
 
2366
    gint count = 0;
 
2367
 
 
2368
    g_return_val_if_fail(GNC_IS_ACCOUNT(account), 0);
 
2369
 
 
2370
    priv = GET_PRIVATE(account);
 
2371
    for (node = priv->children; node; node = g_list_next(node)) {
 
2372
        count += gnc_account_n_descendants(node->data) + 1;
 
2373
    }
 
2374
    return count;
 
2375
}
 
2376
 
 
2377
gint
 
2378
gnc_account_get_current_depth (const Account *account)
 
2379
{
 
2380
    AccountPrivate *priv;
 
2381
    int depth = 0;
 
2382
 
 
2383
    g_return_val_if_fail(GNC_IS_ACCOUNT(account), 0);
 
2384
 
 
2385
    priv = GET_PRIVATE(account);
 
2386
    while (priv->parent && (priv->type != ACCT_TYPE_ROOT)) {
 
2387
        account = priv->parent;
 
2388
        priv = GET_PRIVATE(account);
 
2389
        depth++;
 
2390
    }
 
2391
 
 
2392
    return depth;
 
2393
}
 
2394
 
 
2395
gint
 
2396
gnc_account_get_tree_depth (const Account *account)
 
2397
{
 
2398
  AccountPrivate *priv;
 
2399
  GList *node;
 
2400
  gint depth = 0, child_depth;
 
2401
 
 
2402
  g_return_val_if_fail(GNC_IS_ACCOUNT(account), 0);
 
2403
 
 
2404
  priv = GET_PRIVATE(account);
 
2405
  if (!priv->children)
 
2406
    return 1;
 
2407
 
 
2408
  for (node = priv->children; node; node = g_list_next(node)) {
 
2409
    child_depth = gnc_account_get_tree_depth(node->data);
 
2410
    depth = MAX(depth, child_depth);
 
2411
  }
 
2412
  return depth + 1;
 
2413
}
 
2414
 
 
2415
GList *
 
2416
gnc_account_get_descendants (const Account *account)
 
2417
{
 
2418
  AccountPrivate *priv;
 
2419
  GList *child, *descendants;
 
2420
 
 
2421
  g_return_val_if_fail(GNC_IS_ACCOUNT(account), NULL);
 
2422
 
 
2423
  priv = GET_PRIVATE(account);
 
2424
  if (!priv->children)
 
2425
    return NULL;
 
2426
 
 
2427
  descendants = NULL;
 
2428
  for (child = priv->children; child; child = g_list_next(child)) {
 
2429
    descendants = g_list_append(descendants, child->data);
 
2430
    descendants = g_list_concat(descendants,
 
2431
                                gnc_account_get_descendants(child->data));
 
2432
  }
 
2433
  return descendants;
 
2434
}
 
2435
 
 
2436
GList *
 
2437
gnc_account_get_descendants_sorted (const Account *account)
 
2438
{
 
2439
  AccountPrivate *priv;
 
2440
  GList *child, *children, *descendants;
 
2441
 
 
2442
  /* errors */
 
2443
  g_return_val_if_fail(GNC_IS_ACCOUNT(account), NULL);
 
2444
 
 
2445
  /* optimizations */
 
2446
  priv = GET_PRIVATE(account);
 
2447
  if (!priv->children)
 
2448
    return NULL;
 
2449
 
 
2450
  descendants = NULL;
 
2451
  children = g_list_sort(g_list_copy(priv->children), (GCompareFunc)xaccAccountOrder);
 
2452
  for (child = children; child; child = g_list_next(child)) {
 
2453
    descendants = g_list_append(descendants, child->data);
 
2454
    descendants = g_list_concat(descendants,
 
2455
                                gnc_account_get_descendants(child->data));
 
2456
  }
 
2457
  g_list_free(children);
 
2458
  return descendants;
 
2459
}
 
2460
 
 
2461
Account *
 
2462
gnc_account_lookup_by_name (const Account *parent, const char * name)
 
2463
{
 
2464
  AccountPrivate *cpriv, *ppriv;
 
2465
  Account *child, *result;
 
2466
  GList *node;
 
2467
 
 
2468
  g_return_val_if_fail(GNC_IS_ACCOUNT(parent), NULL);
 
2469
  g_return_val_if_fail(name, NULL);
 
2470
 
 
2471
  /* first, look for accounts hanging off the current node */
 
2472
  ppriv = GET_PRIVATE(parent);
 
2473
  for (node = ppriv->children; node; node = node->next)
 
2474
  {
 
2475
    child = node->data;
 
2476
    cpriv = GET_PRIVATE(child);
 
2477
    if (safe_strcmp(cpriv->accountName, name) == 0)
 
2478
      return child;
 
2479
  }
 
2480
 
 
2481
  /* if we are still here, then we haven't found the account yet.
 
2482
   * Recursively search each of the child accounts next */
 
2483
  for (node = ppriv->children; node; node = node->next)
 
2484
  {
 
2485
    child = node->data;
 
2486
    result = gnc_account_lookup_by_name (child, name);
 
2487
    if (result)
 
2488
      return result;
 
2489
  }
 
2490
 
 
2491
  return NULL;
 
2492
}
 
2493
 
 
2494
/********************************************************************\
 
2495
 * Fetch an account, given its full name                            *
 
2496
\********************************************************************/
 
2497
 
 
2498
static Account *
 
2499
gnc_account_lookup_by_full_name_helper (const Account *parent,
 
2500
                                        gchar **names)
 
2501
{
 
2502
  const AccountPrivate *priv, *ppriv;
 
2503
  Account *found;
 
2504
  GList *node;
 
2505
 
 
2506
  g_return_val_if_fail(GNC_IS_ACCOUNT(parent), NULL);
 
2507
  g_return_val_if_fail(names, NULL);
 
2508
 
 
2509
  /* Look for the first name in the children. */
 
2510
  ppriv = GET_PRIVATE(parent);
 
2511
  for (node = ppriv->children; node; node = node->next) {
 
2512
    Account *account = node->data;
 
2513
 
 
2514
    priv = GET_PRIVATE(account);
 
2515
    if (safe_strcmp(priv->accountName, names[0]) == 0) {
 
2516
      /* We found an account.  If the next entry is NULL, there is
 
2517
       * nothing left in the name, so just return the account. */
 
2518
      if (names[1] == NULL)
 
2519
        return account;
 
2520
 
 
2521
      /* No children?  We're done. */
 
2522
      if (!priv->children)
 
2523
        return NULL;
 
2524
 
 
2525
      /* There's stuff left to search for.  Search recursively. */
 
2526
      found = gnc_account_lookup_by_full_name_helper(account, &names[1]);
 
2527
      if (found != NULL) {
 
2528
        return found;
 
2529
      }
 
2530
    }
 
2531
  }
 
2532
 
 
2533
  return NULL;
 
2534
}
 
2535
 
 
2536
 
 
2537
Account *
 
2538
gnc_account_lookup_by_full_name (const Account *any_acc,
 
2539
                                 const gchar *name)
 
2540
{
 
2541
  const AccountPrivate *rpriv;
 
2542
  const Account *root;
 
2543
  Account *found;
 
2544
  gchar **names;
 
2545
 
 
2546
  g_return_val_if_fail(GNC_IS_ACCOUNT(any_acc), NULL);
 
2547
  g_return_val_if_fail(name, NULL);
 
2548
 
 
2549
  root = any_acc;
 
2550
  rpriv = GET_PRIVATE(root);
 
2551
  while (rpriv->parent) {
 
2552
    root = rpriv->parent;
 
2553
    rpriv = GET_PRIVATE(root);
 
2554
  }
 
2555
  names = g_strsplit(name, gnc_get_account_separator_string(), -1);
 
2556
  found = gnc_account_lookup_by_full_name_helper(root, names);
 
2557
  g_strfreev(names);
 
2558
  return found;
 
2559
}
 
2560
 
 
2561
void
 
2562
gnc_account_foreach_child (const Account *acc,
 
2563
                           AccountCb thunk,
 
2564
                           gpointer user_data)
 
2565
{
 
2566
    const AccountPrivate *priv;
 
2567
    GList *node;
 
2568
 
 
2569
    g_return_if_fail(GNC_IS_ACCOUNT(acc));
 
2570
    g_return_if_fail(thunk);
 
2571
 
 
2572
    priv = GET_PRIVATE(acc);
 
2573
    for (node = priv->children; node; node = node->next) {
 
2574
        thunk (node->data, user_data);
 
2575
    }
 
2576
}
 
2577
 
 
2578
gpointer
 
2579
gnc_account_foreach_child_until (const Account *acc,
 
2580
                                 AccountCb2 thunk,
 
2581
                                 gpointer user_data)
 
2582
{
 
2583
    const AccountPrivate *priv;
 
2584
    GList *node;
 
2585
    gpointer result;
 
2586
 
 
2587
    g_return_val_if_fail(GNC_IS_ACCOUNT(acc), NULL);
 
2588
    g_return_val_if_fail(thunk, NULL);
 
2589
 
 
2590
    priv = GET_PRIVATE(acc);
 
2591
    for (node = priv->children; node; node = node->next) {
 
2592
        result = thunk (node->data, user_data);
 
2593
        if (result)
 
2594
            return(result);
 
2595
    }
 
2596
 
 
2597
    return NULL;
 
2598
}
 
2599
 
 
2600
void
 
2601
gnc_account_foreach_descendant (const Account *acc,
 
2602
                                AccountCb thunk,
 
2603
                                gpointer user_data)
 
2604
{
 
2605
    const AccountPrivate *priv;
 
2606
    GList *node;
 
2607
    Account *child;
 
2608
 
 
2609
    g_return_if_fail(GNC_IS_ACCOUNT(acc));
 
2610
    g_return_if_fail(thunk);
 
2611
 
 
2612
    priv = GET_PRIVATE(acc);
 
2613
    for (node = priv->children; node; node = node->next) {
 
2614
        child = node->data;
 
2615
        thunk(child, user_data);
 
2616
        gnc_account_foreach_descendant(child, thunk, user_data);
 
2617
    }
 
2618
}
 
2619
 
 
2620
gpointer
 
2621
gnc_account_foreach_descendant_until (const Account *acc,
 
2622
                                      AccountCb2 thunk,
 
2623
                                      gpointer user_data)
 
2624
{
 
2625
    const AccountPrivate *priv;
 
2626
    GList *node;
 
2627
    Account *child;
 
2628
    gpointer result;
 
2629
 
 
2630
    g_return_val_if_fail(GNC_IS_ACCOUNT(acc), NULL);
 
2631
    g_return_val_if_fail(thunk, NULL);
 
2632
 
 
2633
    priv = GET_PRIVATE(acc);
 
2634
    for (node = priv->children; node; node = node->next) {
 
2635
        child = node->data;
 
2636
        result = thunk(child, user_data);
 
2637
        if (result)
 
2638
            return(result);
 
2639
 
 
2640
        result = gnc_account_foreach_descendant_until(child, thunk, user_data);
 
2641
        if (result)
 
2642
            return(result);
 
2643
    }
 
2644
 
 
2645
    return NULL;
 
2646
}
 
2647
 
 
2648
 
 
2649
GNCAccountType
 
2650
xaccAccountGetType (const Account *acc)
 
2651
{
 
2652
    g_return_val_if_fail(GNC_IS_ACCOUNT(acc), ACCT_TYPE_NONE);
 
2653
    return GET_PRIVATE(acc)->type;
 
2654
}
 
2655
 
 
2656
static const char*
 
2657
qofAccountGetTypeString (const Account *acc)
 
2658
{
 
2659
    g_return_val_if_fail(GNC_IS_ACCOUNT(acc), NULL);
 
2660
    return xaccAccountTypeEnumAsString(GET_PRIVATE(acc)->type);
 
2661
}
 
2662
 
 
2663
static void
 
2664
qofAccountSetType (Account *acc, const char *type_string)
 
2665
{
 
2666
   g_return_if_fail(GNC_IS_ACCOUNT(acc));
 
2667
   g_return_if_fail(type_string);
 
2668
   xaccAccountSetType(acc, xaccAccountStringToEnum(type_string));
 
2669
}
 
2670
 
 
2671
const char *
 
2672
xaccAccountGetName (const Account *acc)
 
2673
{
 
2674
    g_return_val_if_fail(GNC_IS_ACCOUNT(acc), NULL);
 
2675
    return GET_PRIVATE(acc)->accountName;
 
2676
}
 
2677
 
 
2678
char *
 
2679
xaccAccountGetFullName(const Account *account)
 
2680
{
 
2681
  AccountPrivate *priv;
 
2682
  const Account *a;
 
2683
  char *fullname;
 
2684
  gchar **names;
 
2685
  int level;
 
2686
 
 
2687
  /* So much for hardening the API. Too many callers to this function don't
 
2688
   * bother to check if they have a non-NULL pointer before calling. */
 
2689
  if (NULL == account)
 
2690
      return g_strdup("");
 
2691
 
 
2692
  /* errors */
 
2693
  g_return_val_if_fail(GNC_IS_ACCOUNT(account), g_strdup(""));
 
2694
 
 
2695
  /* optimizations */
 
2696
  priv = GET_PRIVATE(account);
 
2697
  if (!priv->parent)
 
2698
    return g_strdup("");
 
2699
 
 
2700
  /* Figure out how much space is needed by counting the nodes up to
 
2701
   * the root. */
 
2702
  level = 0;
 
2703
  for (a = account; a; a = priv->parent) {
 
2704
    priv = GET_PRIVATE(a);
 
2705
    level++;
 
2706
  }
 
2707
 
 
2708
  /* Get all the pointers in the right order. The root node "entry"
 
2709
   * becomes the terminating NULL pointer for the array of strings. */
 
2710
  names = g_malloc(level * sizeof(gchar *));
 
2711
  names[--level] = NULL;
 
2712
  for (a = account; level > 0; a = priv->parent) {
 
2713
    priv = GET_PRIVATE(a);
 
2714
    names[--level] = priv->accountName;
 
2715
  }
 
2716
 
 
2717
  /* Build the full name */
 
2718
  fullname =  g_strjoinv(account_separator, names);
 
2719
  g_free(names);
 
2720
 
 
2721
  return fullname;
 
2722
}
 
2723
 
 
2724
const char *
 
2725
xaccAccountGetCode (const Account *acc)
 
2726
{
 
2727
    g_return_val_if_fail(GNC_IS_ACCOUNT(acc), NULL);
 
2728
    return GET_PRIVATE(acc)->accountCode;
 
2729
}
 
2730
 
 
2731
const char * 
 
2732
xaccAccountGetDescription (const Account *acc)
 
2733
{
 
2734
    g_return_val_if_fail(GNC_IS_ACCOUNT(acc), NULL);
 
2735
    return GET_PRIVATE(acc)->description;
 
2736
}
 
2737
 
 
2738
const char * 
 
2739
xaccAccountGetNotes (const Account *acc) 
 
2740
{
 
2741
    g_return_val_if_fail(GNC_IS_ACCOUNT(acc), NULL);
 
2742
    return acc ? kvp_frame_get_string(acc->inst.kvp_data, "notes") : NULL;
 
2743
}
 
2744
 
 
2745
gnc_commodity * 
 
2746
DxaccAccountGetCurrency (const Account *acc)
 
2747
{
 
2748
  KvpValue *v;
 
2749
  const char *s;
 
2750
  gnc_commodity_table *table;
 
2751
 
 
2752
  if (!acc) return NULL;
 
2753
 
 
2754
  v = kvp_frame_get_slot(acc->inst.kvp_data, "old-currency");
 
2755
  if (!v) return NULL;
 
2756
 
 
2757
  s = kvp_value_get_string (v);
 
2758
  if (!s) return NULL;
 
2759
 
 
2760
  table = gnc_commodity_table_get_table (qof_instance_get_book(acc));
 
2761
 
 
2762
  return gnc_commodity_table_lookup_unique (table, s);
 
2763
}
 
2764
 
 
2765
gnc_commodity * 
 
2766
xaccAccountGetCommodity (const Account *acc)
 
2767
{
 
2768
    if (!GNC_IS_ACCOUNT(acc))
 
2769
        return NULL;
 
2770
    return GET_PRIVATE(acc)->commodity;
 
2771
}
 
2772
 
 
2773
/********************************************************************\
 
2774
\********************************************************************/
 
2775
 
 
2776
gnc_numeric 
 
2777
gnc_account_get_start_balance (Account *acc)
 
2778
{
 
2779
  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
 
2780
 
 
2781
  return GET_PRIVATE(acc)->starting_balance;
 
2782
}
 
2783
 
 
2784
void 
 
2785
gnc_account_set_start_balance (Account *acc, const gnc_numeric start_baln)
 
2786
{
 
2787
  AccountPrivate *priv;
 
2788
 
 
2789
  g_return_if_fail(GNC_IS_ACCOUNT(acc));
 
2790
 
 
2791
  priv = GET_PRIVATE(acc);
 
2792
  priv->starting_balance = start_baln;
 
2793
  priv->balance_dirty = TRUE;
 
2794
}
 
2795
 
 
2796
gnc_numeric 
 
2797
gnc_account_get_start_cleared_balance (Account *acc)
 
2798
{
 
2799
  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
 
2800
 
 
2801
  return GET_PRIVATE(acc)->starting_cleared_balance;
 
2802
}
 
2803
 
 
2804
void 
 
2805
gnc_account_set_start_cleared_balance (Account *acc,
 
2806
                                       const gnc_numeric start_baln)
 
2807
{
 
2808
  AccountPrivate *priv;
 
2809
 
 
2810
  g_return_if_fail(GNC_IS_ACCOUNT(acc));
 
2811
 
 
2812
  priv = GET_PRIVATE(acc);
 
2813
  priv->starting_balance = start_baln;
 
2814
  priv->balance_dirty = TRUE;
 
2815
}
 
2816
 
 
2817
gnc_numeric 
 
2818
gnc_account_get_start_reconciled_balance (Account *acc)
 
2819
{
 
2820
  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
 
2821
 
 
2822
  return GET_PRIVATE(acc)->starting_reconciled_balance;
 
2823
}
 
2824
 
 
2825
void 
 
2826
gnc_account_set_start_reconciled_balance (Account *acc,
 
2827
                                          const gnc_numeric start_baln)
 
2828
{
 
2829
  AccountPrivate *priv;
 
2830
 
 
2831
  g_return_if_fail(GNC_IS_ACCOUNT(acc));
 
2832
 
 
2833
  priv = GET_PRIVATE(acc);
 
2834
  priv->starting_balance = start_baln;
 
2835
  priv->balance_dirty = TRUE;
 
2836
}
 
2837
 
 
2838
gnc_numeric
 
2839
xaccAccountGetBalance (const Account *acc) 
 
2840
{
 
2841
    g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
 
2842
    return GET_PRIVATE(acc)->balance;
 
2843
}
 
2844
 
 
2845
gnc_numeric
 
2846
xaccAccountGetClearedBalance (const Account *acc)
 
2847
{
 
2848
    g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
 
2849
    return GET_PRIVATE(acc)->cleared_balance;
 
2850
}
 
2851
 
 
2852
gnc_numeric
 
2853
xaccAccountGetReconciledBalance (const Account *acc)
 
2854
{
 
2855
    g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
 
2856
    return GET_PRIVATE(acc)->reconciled_balance;
 
2857
}
 
2858
 
 
2859
gnc_numeric
 
2860
xaccAccountGetProjectedMinimumBalance (const Account *acc)
 
2861
{
 
2862
  AccountPrivate *priv;
 
2863
  GList *node;
 
2864
  time_t today;
 
2865
  gnc_numeric lowest = gnc_numeric_zero ();
 
2866
  int seen_a_transaction = 0;
 
2867
 
 
2868
  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
 
2869
 
 
2870
  priv = GET_PRIVATE(acc);
 
2871
  today = gnc_timet_get_today_end();
 
2872
  for (node = g_list_last(priv->splits); node; node = node->prev)
 
2873
  {
 
2874
    Split *split = node->data;
 
2875
 
 
2876
    if (!seen_a_transaction)
 
2877
    {
 
2878
      lowest = xaccSplitGetBalance (split);
 
2879
      seen_a_transaction = 1;
 
2880
    } else if (gnc_numeric_compare(xaccSplitGetBalance (split), lowest) < 0) {
 
2881
      lowest = xaccSplitGetBalance (split);
 
2882
    }
 
2883
 
 
2884
    if (xaccTransGetDate (xaccSplitGetParent (split)) <= today)
 
2885
      return lowest;
 
2886
  }
 
2887
 
 
2888
  return lowest;
 
2889
}
 
2890
 
 
2891
 
 
2892
/********************************************************************\
 
2893
\********************************************************************/
 
2894
 
 
2895
gnc_numeric
 
2896
xaccAccountGetBalanceAsOfDate (Account *acc, time_t date)
 
2897
{
 
2898
  /* Ideally this could use xaccAccountForEachSplit, but
 
2899
   * it doesn't exist yet and I'm uncertain of exactly how
 
2900
   * it would work at this time, since it differs from
 
2901
   * xaccAccountForEachTransaction by using gpointer return
 
2902
   * values rather than gints.
 
2903
   */
 
2904
  AccountPrivate *priv;
 
2905
  GList   *lp;
 
2906
  Timespec ts, trans_ts;
 
2907
  gboolean found = FALSE;
 
2908
  gnc_numeric balance;
 
2909
 
 
2910
  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
 
2911
 
 
2912
  xaccAccountSortSplits (acc, TRUE); /* just in case, normally a noop */
 
2913
  xaccAccountRecomputeBalance (acc); /* just in case, normally a noop */
 
2914
 
 
2915
  priv = GET_PRIVATE(acc);
 
2916
  balance = priv->balance;
 
2917
 
 
2918
  /* Since transaction post times are stored as a Timespec,
 
2919
   * convert date into a Timespec as well rather than converting
 
2920
   * each transaction's Timespec into a time_t.
 
2921
   *
 
2922
   * FIXME: CAS: I think this comment is a bogus justification for
 
2923
   * using xaccTransGetDatePostedTS.  There's no benefit to using
 
2924
   * Timespec when the input argument is time_t, and it's hard to
 
2925
   * imagine that casting long long to long and comparing two longs is
 
2926
   * worse than comparing two long longs every time.  IMO,
 
2927
   * xaccAccountGetPresentBalance gets this right, and its algorithm
 
2928
   * should be used here.
 
2929
   */
 
2930
  ts.tv_sec = date;
 
2931
  ts.tv_nsec = 0;
 
2932
 
 
2933
  lp = priv->splits;
 
2934
  while( lp && !found )
 
2935
  {
 
2936
    xaccTransGetDatePostedTS( xaccSplitGetParent( (Split *)lp->data ),
 
2937
                              &trans_ts );
 
2938
    if( timespec_cmp( &trans_ts, &ts ) >= 0 )
 
2939
      found = TRUE;
 
2940
    else
 
2941
      lp = lp->next;
 
2942
  }
 
2943
 
 
2944
  if( lp ) {
 
2945
    if ( lp->prev ) {
 
2946
      /* Since lp is now pointing to a split which was past the reconcile
 
2947
       * date, get the running balance of the previous split.
 
2948
       */
 
2949
      balance = xaccSplitGetBalance( (Split *)lp->prev->data );
 
2950
    }           
 
2951
    else {
 
2952
      /* AsOf date must be before any entries, return zero. */
 
2953
      balance = gnc_numeric_zero();
 
2954
    }
 
2955
  }
 
2956
 
 
2957
  /* Otherwise there were no splits posted after the given date,
 
2958
   * so the latest account balance should be good enough.
 
2959
   */
 
2960
 
 
2961
  return( balance );
 
2962
}
 
2963
 
 
2964
/*
 
2965
 * Originally gsr_account_present_balance in gnc-split-reg.c
 
2966
 *
 
2967
 * How does this routine compare to xaccAccountGetBalanceAsOfDate just
 
2968
 * above?  These two routines should eventually be collapsed into one.
 
2969
 * Perhaps the startup logic from that one, and the logic from this
 
2970
 * one that walks from the tail of the split list.
 
2971
 */
 
2972
gnc_numeric
 
2973
xaccAccountGetPresentBalance (const Account *acc)
 
2974
{
 
2975
  AccountPrivate *priv;
 
2976
  GList *node;
 
2977
  time_t today;
 
2978
 
 
2979
  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
 
2980
 
 
2981
  priv = GET_PRIVATE(acc);
 
2982
  today = gnc_timet_get_today_end();
 
2983
  for (node = g_list_last(priv->splits); node; node = node->prev)
 
2984
  {
 
2985
    Split *split = node->data;
 
2986
 
 
2987
    if (xaccTransGetDate (xaccSplitGetParent (split)) <= today)
 
2988
      return xaccSplitGetBalance (split);
 
2989
  }
 
2990
 
 
2991
  return gnc_numeric_zero ();
 
2992
}
 
2993
 
 
2994
 
 
2995
/********************************************************************\
 
2996
\********************************************************************/
 
2997
/* XXX TODO: These 'GetBal' routines should be moved to some
 
2998
 * utility area outside of the core account engine area. 
 
2999
 */
 
3000
 
 
3001
/*
 
3002
 * Convert a balance from one currency to another.
 
3003
 */
 
3004
gnc_numeric
 
3005
xaccAccountConvertBalanceToCurrency(const Account *acc, /* for book */
 
3006
                                    gnc_numeric balance,
 
3007
                                    const gnc_commodity *balance_currency,
 
3008
                                    const gnc_commodity *new_currency)
 
3009
{
 
3010
  QofBook *book;
 
3011
  GNCPriceDB *pdb;
 
3012
 
 
3013
  if (gnc_numeric_zero_p (balance) ||
 
3014
      gnc_commodity_equiv (balance_currency, new_currency))
 
3015
    return balance;
 
3016
 
 
3017
  book = gnc_account_get_book (acc);
 
3018
  pdb = gnc_pricedb_get_db (book);
 
3019
 
 
3020
  balance = gnc_pricedb_convert_balance_latest_price(
 
3021
      pdb, balance, balance_currency, new_currency);
 
3022
 
 
3023
  return balance;
 
3024
}
 
3025
 
 
3026
/*
 
3027
 * Convert a balance from one currency to another with price of
 
3028
 * a given date.
 
3029
 */
 
3030
gnc_numeric
 
3031
xaccAccountConvertBalanceToCurrencyAsOfDate(const Account *acc, /* for book */
 
3032
                                            gnc_numeric balance,
 
3033
                                            gnc_commodity *balance_currency,
 
3034
                                            gnc_commodity *new_currency,
 
3035
                                            time_t date)
 
3036
{
 
3037
  QofBook *book;
 
3038
  GNCPriceDB *pdb;
 
3039
  Timespec ts;
 
3040
 
 
3041
  if (gnc_numeric_zero_p (balance) ||
 
3042
      gnc_commodity_equiv (balance_currency, new_currency))
 
3043
    return balance;
 
3044
 
 
3045
  book = gnc_account_get_book (acc);
 
3046
  pdb = gnc_book_get_pricedb (book);
 
3047
 
 
3048
  ts.tv_sec = date;
 
3049
  ts.tv_nsec = 0;
 
3050
 
 
3051
  balance = gnc_pricedb_convert_balance_nearest_price(
 
3052
      pdb, balance, balance_currency, new_currency, ts);
 
3053
 
 
3054
  return balance;
 
3055
}
 
3056
 
 
3057
/*
 
3058
 * Given an account and a GetBalanceFn pointer, extract the requested
 
3059
 * balance from the account and then convert it to the desired
 
3060
 * currency.
 
3061
 */
 
3062
static gnc_numeric
 
3063
xaccAccountGetXxxBalanceInCurrency (const Account *acc,
 
3064
                                    xaccGetBalanceFn fn,
 
3065
                                    const gnc_commodity *report_currency)
 
3066
{
 
3067
    AccountPrivate *priv;
 
3068
    gnc_numeric balance;
 
3069
 
 
3070
    g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
 
3071
    g_return_val_if_fail(fn, gnc_numeric_zero());
 
3072
    g_return_val_if_fail(GNC_IS_COMMODITY(report_currency), gnc_numeric_zero());
 
3073
 
 
3074
    priv = GET_PRIVATE(acc);
 
3075
    balance = fn(acc);
 
3076
    balance = xaccAccountConvertBalanceToCurrency(acc, balance,
 
3077
                                                  priv->commodity,
 
3078
                                                  report_currency);
 
3079
    return balance;
 
3080
}
 
3081
 
 
3082
static gnc_numeric
 
3083
xaccAccountGetXxxBalanceAsOfDateInCurrency(Account *acc, time_t date,
 
3084
                                           xaccGetBalanceAsOfDateFn fn,
 
3085
                                           const gnc_commodity *report_commodity)
 
3086
{
 
3087
    AccountPrivate *priv;
 
3088
 
 
3089
    g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
 
3090
    g_return_val_if_fail(fn, gnc_numeric_zero());
 
3091
    g_return_val_if_fail(GNC_IS_COMMODITY(report_commodity), gnc_numeric_zero());
 
3092
 
 
3093
    priv = GET_PRIVATE(acc);
 
3094
    return xaccAccountConvertBalanceToCurrency(
 
3095
        acc, fn(acc, date), priv->commodity, report_commodity);
 
3096
}
 
3097
 
 
3098
/*
 
3099
 * Data structure used to pass various arguments into the following fn.
 
3100
 */
 
3101
typedef struct
 
3102
{
 
3103
  const gnc_commodity *currency;
 
3104
  gnc_numeric balance;
 
3105
  xaccGetBalanceFn fn;
 
3106
  xaccGetBalanceAsOfDateFn asOfDateFn;
 
3107
  time_t date;
 
3108
} CurrencyBalance;
 
3109
 
 
3110
 
 
3111
/*
 
3112
 * A helper function for iterating over all the accounts in a list or
 
3113
 * tree.  This function is called once per account, and sums up the
 
3114
 * values of all these accounts.
 
3115
 */
 
3116
static void
 
3117
xaccAccountBalanceHelper (Account *acc, gpointer data)
 
3118
{
 
3119
  CurrencyBalance *cb = data;
 
3120
  gnc_numeric balance;
 
3121
 
 
3122
  if (!cb->fn || !cb->currency)
 
3123
    return;
 
3124
  balance = xaccAccountGetXxxBalanceInCurrency (acc, cb->fn, cb->currency);
 
3125
  cb->balance = gnc_numeric_add (cb->balance, balance,
 
3126
                                 gnc_commodity_get_fraction (cb->currency),
 
3127
                                 GNC_HOW_RND_ROUND);
 
3128
}
 
3129
 
 
3130
static void
 
3131
xaccAccountBalanceAsOfDateHelper (Account *acc, gpointer data)
 
3132
{
 
3133
    CurrencyBalance *cb = data;
 
3134
    gnc_numeric balance;
 
3135
 
 
3136
    g_return_if_fail (cb->asOfDateFn && cb->currency);
 
3137
 
 
3138
    balance = xaccAccountGetXxxBalanceAsOfDateInCurrency (
 
3139
        acc, cb->date, cb->asOfDateFn, cb->currency);
 
3140
    cb->balance = gnc_numeric_add (cb->balance, balance,
 
3141
                                   gnc_commodity_get_fraction (cb->currency),
 
3142
                                   GNC_HOW_RND_ROUND);
 
3143
}
 
3144
 
 
3145
 
 
3146
 
 
3147
/*
 
3148
 * Common function that iterates recursively over all accounts below
 
3149
 * the specified account.  It uses xaccAccountBalanceHelper to sum up
 
3150
 * the balances of all its children, and uses the specified function
 
3151
 * 'fn' for extracting the balance.  This function may extract the
 
3152
 * current value, the reconciled value, etc.
 
3153
 *
 
3154
 * If 'report_commodity' is NULL, just use the account's commodity.
 
3155
 * If 'include_children' is FALSE, this function doesn't recurse at all.
 
3156
 */
 
3157
static gnc_numeric
 
3158
xaccAccountGetXxxBalanceInCurrencyRecursive (const Account *acc,
 
3159
                                             xaccGetBalanceFn fn,
 
3160
                                             const gnc_commodity *report_commodity,
 
3161
                                             gboolean include_children)
 
3162
{
 
3163
  gnc_numeric balance;
 
3164
 
 
3165
  if (!acc) return gnc_numeric_zero ();
 
3166
  if (!report_commodity)
 
3167
    report_commodity = xaccAccountGetCommodity (acc);
 
3168
  if (!report_commodity)
 
3169
    return gnc_numeric_zero();
 
3170
 
 
3171
  balance = xaccAccountGetXxxBalanceInCurrency (acc, fn, report_commodity);
 
3172
 
 
3173
  /* If needed, sum up the children converting to the *requested*
 
3174
     commodity. */
 
3175
  if (include_children) {
 
3176
    CurrencyBalance cb = { report_commodity, balance, fn, NULL, 0 };
 
3177
 
 
3178
    gnc_account_foreach_descendant (acc, xaccAccountBalanceHelper, &cb);
 
3179
    balance = cb.balance;
 
3180
  }
 
3181
 
 
3182
  return balance;
 
3183
}
 
3184
 
 
3185
static gnc_numeric
 
3186
xaccAccountGetXxxBalanceAsOfDateInCurrencyRecursive (
 
3187
    Account *acc, time_t date, xaccGetBalanceAsOfDateFn fn,
 
3188
    gnc_commodity *report_commodity, gboolean include_children)
 
3189
{
 
3190
  gnc_numeric balance;
 
3191
 
 
3192
  g_return_val_if_fail(acc, gnc_numeric_zero());
 
3193
  if (!report_commodity)
 
3194
      report_commodity = xaccAccountGetCommodity (acc);
 
3195
  if (!report_commodity)
 
3196
    return gnc_numeric_zero();
 
3197
 
 
3198
  balance = xaccAccountGetXxxBalanceAsOfDateInCurrency(
 
3199
      acc, date, fn, report_commodity);
 
3200
 
 
3201
  /* If needed, sum up the children converting to the *requested*
 
3202
     commodity. */
 
3203
  if (include_children) {
 
3204
    CurrencyBalance cb = { report_commodity, balance, NULL, fn, date };
 
3205
 
 
3206
    gnc_account_foreach_descendant (acc, xaccAccountBalanceAsOfDateHelper, &cb);
 
3207
    balance = cb.balance;
 
3208
  }
 
3209
 
 
3210
  return balance;
 
3211
}
 
3212
 
 
3213
gnc_numeric
 
3214
xaccAccountGetBalanceInCurrency (const Account *acc, 
 
3215
                                 const gnc_commodity *report_commodity,
 
3216
                                 gboolean include_children)
 
3217
{
 
3218
  gnc_numeric rc;
 
3219
  rc = xaccAccountGetXxxBalanceInCurrencyRecursive (
 
3220
      acc, xaccAccountGetBalance, report_commodity, include_children);
 
3221
  PINFO(" baln=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, rc.num, rc.denom);
 
3222
  return rc;
 
3223
}
 
3224
 
 
3225
 
 
3226
gnc_numeric
 
3227
xaccAccountGetClearedBalanceInCurrency (const Account *acc,
 
3228
                                        const gnc_commodity *report_commodity,
 
3229
                                        gboolean include_children)
 
3230
{
 
3231
  return xaccAccountGetXxxBalanceInCurrencyRecursive (
 
3232
      acc, xaccAccountGetClearedBalance, report_commodity,
 
3233
      include_children);
 
3234
}
 
3235
 
 
3236
 
 
3237
gnc_numeric
 
3238
xaccAccountGetReconciledBalanceInCurrency (const Account *acc,
 
3239
                                           const gnc_commodity *report_commodity,
 
3240
                                           gboolean include_children)
 
3241
{
 
3242
  return xaccAccountGetXxxBalanceInCurrencyRecursive (
 
3243
      acc, xaccAccountGetReconciledBalance, report_commodity,
 
3244
      include_children);
 
3245
}
 
3246
 
 
3247
gnc_numeric
 
3248
xaccAccountGetPresentBalanceInCurrency (const Account *acc,
 
3249
                                        const gnc_commodity *report_commodity,
 
3250
                                        gboolean include_children)
 
3251
{
 
3252
  return xaccAccountGetXxxBalanceInCurrencyRecursive (
 
3253
      acc, xaccAccountGetPresentBalance, report_commodity,
 
3254
      include_children);
 
3255
}
 
3256
 
 
3257
gnc_numeric
 
3258
xaccAccountGetProjectedMinimumBalanceInCurrency (
 
3259
    const Account *acc, 
 
3260
    const gnc_commodity *report_commodity,
 
3261
    gboolean include_children)
 
3262
{
 
3263
  return xaccAccountGetXxxBalanceInCurrencyRecursive (
 
3264
      acc, xaccAccountGetProjectedMinimumBalance, report_commodity,
 
3265
      include_children);
 
3266
}
 
3267
 
 
3268
gnc_numeric
 
3269
xaccAccountGetBalanceAsOfDateInCurrency(
 
3270
    Account *acc, time_t date, gnc_commodity *report_commodity,
 
3271
    gboolean include_children)
 
3272
{
 
3273
    return xaccAccountGetXxxBalanceAsOfDateInCurrencyRecursive (
 
3274
        acc, date, xaccAccountGetBalanceAsOfDate, report_commodity,
 
3275
        include_children);
 
3276
}
 
3277
 
 
3278
gnc_numeric
 
3279
xaccAccountGetBalanceChangeForPeriod (Account *acc, time_t t1, time_t t2, gboolean recurse)
 
3280
{
 
3281
  gnc_numeric b1, b2;  
 
3282
 
 
3283
  b1 = xaccAccountGetBalanceAsOfDateInCurrency(acc, t1, NULL, recurse);
 
3284
  b2 = xaccAccountGetBalanceAsOfDateInCurrency(acc, t2, NULL, recurse);
 
3285
  return gnc_numeric_sub(b2, b1, GNC_DENOM_AUTO, GNC_HOW_DENOM_FIXED);
 
3286
}
 
3287
 
 
3288
 
 
3289
/********************************************************************\
 
3290
\********************************************************************/
 
3291
 
 
3292
/* THIS API NEEDS TO CHANGE.
 
3293
 *
 
3294
 * This code exposes the internal structure of the account object to
 
3295
 * external callers by returning the actual list used by the object.
 
3296
 * It should instead return a copy of the split list that the caller
 
3297
 * is required to free.  That change would provide the freedom of
 
3298
 * allowing the internal organization to change data structures if
 
3299
 * necessary for whatever reason, while leaving the external API
 
3300
 * unchanged. */
 
3301
SplitList *
 
3302
xaccAccountGetSplitList (const Account *acc) 
 
3303
{
 
3304
    g_return_val_if_fail(GNC_IS_ACCOUNT(acc), NULL);
 
3305
    return GET_PRIVATE(acc)->splits;
 
3306
}
 
3307
 
 
3308
LotList *
 
3309
xaccAccountGetLotList (const Account *acc) 
 
3310
{
 
3311
    g_return_val_if_fail(GNC_IS_ACCOUNT(acc), NULL);
 
3312
    return g_list_copy(GET_PRIVATE(acc)->lots);
 
3313
}
 
3314
 
 
3315
LotList *
 
3316
xaccAccountFindOpenLots (const Account *acc,
 
3317
                         gboolean (*match_func)(GNCLot *lot,
 
3318
                                                gpointer user_data),
 
3319
                         gpointer user_data, GCompareFunc sort_func)
 
3320
{
 
3321
  AccountPrivate *priv;
 
3322
  GList *lot_list;
 
3323
  GList *retval = NULL;
 
3324
 
 
3325
  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), NULL);
 
3326
 
 
3327
  priv = GET_PRIVATE(acc);
 
3328
  for (lot_list = priv->lots; lot_list; lot_list = lot_list->next) {
 
3329
    GNCLot *lot = lot_list->data;
 
3330
 
 
3331
    /* If this lot is closed, then ignore it */
 
3332
    if (gnc_lot_is_closed (lot))
 
3333
      continue;
 
3334
 
 
3335
    if (match_func && !(match_func)(lot, user_data))
 
3336
      continue;
 
3337
 
 
3338
    /* Ok, this is a valid lot.  Add it to our list of lots */
 
3339
    if (sort_func)
 
3340
      retval = g_list_insert_sorted (retval, lot, sort_func);
 
3341
    else
 
3342
      retval = g_list_prepend (retval, lot);
 
3343
  }
 
3344
 
 
3345
  return retval;
 
3346
}
 
3347
 
 
3348
gpointer
 
3349
xaccAccountForEachLot(const Account *acc, 
 
3350
                      gpointer (*proc)(GNCLot *lot, void *data), void *data) 
 
3351
{
 
3352
  AccountPrivate *priv;
 
3353
  LotList *node;
 
3354
  gpointer result = NULL;
 
3355
 
 
3356
  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), NULL);
 
3357
  g_return_val_if_fail(proc, NULL);
 
3358
 
 
3359
  priv = GET_PRIVATE(acc);
 
3360
  for (node = priv->lots; node; node = node->next)
 
3361
      if ((result = proc((GNCLot *)node->data, data))) 
 
3362
          break;
 
3363
  
 
3364
  return result;
 
3365
}
 
3366
 
 
3367
/********************************************************************\
 
3368
\********************************************************************/
 
3369
 
 
3370
/* These functions use interchange gint64 and gboolean.  Is that right? */
 
3371
gboolean
 
3372
xaccAccountGetTaxRelated (const Account *acc)
 
3373
{
 
3374
  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
 
3375
  return kvp_frame_get_gint64(acc->inst.kvp_data, "tax-related");
 
3376
}
 
3377
 
 
3378
void
 
3379
xaccAccountSetTaxRelated (Account *acc, gboolean tax_related)
 
3380
{
 
3381
  KvpValue *new_value;
 
3382
 
 
3383
  g_return_if_fail(GNC_IS_ACCOUNT(acc));
 
3384
 
 
3385
  if (tax_related)
 
3386
    new_value = kvp_value_new_gint64 (tax_related);
 
3387
  else
 
3388
    new_value = NULL;
 
3389
 
 
3390
  xaccAccountBeginEdit (acc);
 
3391
  kvp_frame_set_slot_nc(acc->inst.kvp_data, "tax-related", new_value);
 
3392
  mark_account (acc);
 
3393
  xaccAccountCommitEdit (acc);
 
3394
}
 
3395
 
 
3396
const char *
 
3397
xaccAccountGetTaxUSCode (const Account *acc)
 
3398
{
 
3399
  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), NULL);
 
3400
  return kvp_frame_get_string(acc->inst.kvp_data, "tax-US/code");
 
3401
}
 
3402
 
 
3403
void
 
3404
xaccAccountSetTaxUSCode (Account *acc, const char *code)
 
3405
{
 
3406
  g_return_if_fail(GNC_IS_ACCOUNT(acc));
 
3407
 
 
3408
  xaccAccountBeginEdit (acc);
 
3409
  kvp_frame_set_string (acc->inst.kvp_data, "/tax-US/code", code);
 
3410
  mark_account (acc);
 
3411
  xaccAccountCommitEdit (acc);
 
3412
}
 
3413
 
 
3414
const char *
 
3415
xaccAccountGetTaxUSPayerNameSource (const Account *acc)
 
3416
{
 
3417
  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), NULL);
 
3418
  return kvp_frame_get_string(acc->inst.kvp_data,
 
3419
                              "tax-US/payer-name-source");
 
3420
}
 
3421
 
 
3422
void
 
3423
xaccAccountSetTaxUSPayerNameSource (Account *acc, const char *source)
 
3424
{
 
3425
  g_return_if_fail(GNC_IS_ACCOUNT(acc));
 
3426
 
 
3427
  xaccAccountBeginEdit (acc);
 
3428
  kvp_frame_set_string (acc->inst.kvp_data, 
 
3429
                        "/tax-US/payer-name-source", source);
 
3430
  mark_account (acc);
 
3431
  xaccAccountCommitEdit (acc);
 
3432
}
 
3433
 
 
3434
/********************************************************************\
 
3435
\********************************************************************/
 
3436
 
 
3437
gboolean
 
3438
xaccAccountGetPlaceholder (const Account *acc)
 
3439
{
 
3440
  const char *str;
 
3441
 
 
3442
  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
 
3443
  
 
3444
  str = kvp_frame_get_string(acc->inst.kvp_data, "placeholder");
 
3445
  return (str && !strcmp(str, "true"));
 
3446
}
 
3447
 
 
3448
void
 
3449
xaccAccountSetPlaceholder (Account *acc, gboolean val)
 
3450
{
 
3451
  g_return_if_fail(GNC_IS_ACCOUNT(acc));
 
3452
  
 
3453
  xaccAccountBeginEdit (acc);
 
3454
  kvp_frame_set_string (acc->inst.kvp_data, 
 
3455
                        "placeholder", val ? "true" : NULL);
 
3456
  mark_account (acc);
 
3457
  xaccAccountCommitEdit (acc);
 
3458
}
 
3459
 
 
3460
GNCPlaceholderType
 
3461
xaccAccountGetDescendantPlaceholder (const Account *acc)
 
3462
{
 
3463
  GList *descendants, *node;
 
3464
  GNCPlaceholderType ret = PLACEHOLDER_NONE;
 
3465
 
 
3466
  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), PLACEHOLDER_NONE);
 
3467
  if (xaccAccountGetPlaceholder(acc)) return PLACEHOLDER_THIS;
 
3468
 
 
3469
  descendants = gnc_account_get_descendants(acc);
 
3470
  for (node = descendants; node; node = node->next) 
 
3471
      if (xaccAccountGetPlaceholder((Account *) node->data)) {
 
3472
          ret = PLACEHOLDER_CHILD;
 
3473
          break;
 
3474
      }
 
3475
 
 
3476
  g_list_free(descendants);
 
3477
  return ret;
 
3478
}
 
3479
 
 
3480
/********************************************************************\
 
3481
\********************************************************************/
 
3482
 
 
3483
gboolean
 
3484
xaccAccountGetHidden (const Account *acc)
 
3485
{
 
3486
  const char *str;
 
3487
 
 
3488
  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
 
3489
  
 
3490
  str = kvp_frame_get_string(acc->inst.kvp_data, "hidden");
 
3491
  return (str && !strcmp(str, "true"));
 
3492
}
 
3493
 
 
3494
void
 
3495
xaccAccountSetHidden (Account *acc, gboolean val)
 
3496
{
 
3497
  g_return_if_fail(GNC_IS_ACCOUNT(acc));
 
3498
  
 
3499
  xaccAccountBeginEdit (acc);
 
3500
  kvp_frame_set_string (acc->inst.kvp_data, "hidden",
 
3501
                        val ? "true" : NULL);
 
3502
  mark_account (acc);
 
3503
  xaccAccountCommitEdit (acc);
 
3504
}
 
3505
 
 
3506
gboolean
 
3507
xaccAccountIsHidden (const Account *acc)
 
3508
{
 
3509
  AccountPrivate *priv;
 
3510
 
 
3511
  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
 
3512
 
 
3513
  if (xaccAccountGetHidden(acc))
 
3514
    return TRUE;
 
3515
  priv = GET_PRIVATE(acc);
 
3516
  while ((acc = priv->parent) != NULL) {
 
3517
    priv = GET_PRIVATE(acc);
 
3518
    if (xaccAccountGetHidden(acc))
 
3519
      return TRUE;
 
3520
  }
 
3521
  return FALSE;
 
3522
}
 
3523
 
 
3524
/********************************************************************\
 
3525
\********************************************************************/
 
3526
 
 
3527
gboolean
 
3528
xaccAccountHasAncestor (const Account *acc, const Account * ancestor)
 
3529
{
 
3530
  const Account *parent;
 
3531
 
 
3532
  g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
 
3533
  g_return_val_if_fail(GNC_IS_ACCOUNT(ancestor), FALSE);
 
3534
 
 
3535
  parent = acc;
 
3536
  while (parent && parent != ancestor)
 
3537
      parent = GET_PRIVATE(parent)->parent;
 
3538
 
 
3539
  return (parent == ancestor);
 
3540
}
 
3541
 
 
3542
/********************************************************************\
 
3543
\********************************************************************/
 
3544
 
 
3545
/* You must edit the functions in this block in tandem.  KEEP THEM IN
 
3546
   SYNC! */
 
3547
 
 
3548
#define GNC_RETURN_ENUM_AS_STRING(x) case (ACCT_TYPE_ ## x): return #x;
 
3549
 
 
3550
const char *
 
3551
xaccAccountTypeEnumAsString(GNCAccountType type) 
 
3552
{
 
3553
  switch(type) 
 
3554
  {
 
3555
    GNC_RETURN_ENUM_AS_STRING(NONE);
 
3556
    GNC_RETURN_ENUM_AS_STRING(BANK);
 
3557
    GNC_RETURN_ENUM_AS_STRING(CASH);
 
3558
    GNC_RETURN_ENUM_AS_STRING(CREDIT);
 
3559
    GNC_RETURN_ENUM_AS_STRING(ASSET);
 
3560
    GNC_RETURN_ENUM_AS_STRING(LIABILITY);
 
3561
    GNC_RETURN_ENUM_AS_STRING(STOCK);
 
3562
    GNC_RETURN_ENUM_AS_STRING(MUTUAL);
 
3563
    GNC_RETURN_ENUM_AS_STRING(CURRENCY);
 
3564
    GNC_RETURN_ENUM_AS_STRING(INCOME);
 
3565
    GNC_RETURN_ENUM_AS_STRING(EXPENSE);
 
3566
    GNC_RETURN_ENUM_AS_STRING(EQUITY);
 
3567
    GNC_RETURN_ENUM_AS_STRING(RECEIVABLE);
 
3568
    GNC_RETURN_ENUM_AS_STRING(PAYABLE);
 
3569
    GNC_RETURN_ENUM_AS_STRING(ROOT);
 
3570
    GNC_RETURN_ENUM_AS_STRING(CHECKING);
 
3571
    GNC_RETURN_ENUM_AS_STRING(SAVINGS);
 
3572
    GNC_RETURN_ENUM_AS_STRING(MONEYMRKT);
 
3573
    GNC_RETURN_ENUM_AS_STRING(CREDITLINE);
 
3574
    default:
 
3575
      PERR ("asked to translate unknown account type %d.\n", type);
 
3576
      break;
 
3577
  }
 
3578
  return(NULL);
 
3579
}
 
3580
 
 
3581
#undef GNC_RETURN_ENUM_AS_STRING
 
3582
 
 
3583
#define GNC_RETURN_ON_MATCH(x) \
 
3584
  if(safe_strcmp(#x, (str)) == 0) { *type = ACCT_TYPE_ ## x; return(TRUE); }
 
3585
 
 
3586
gboolean
 
3587
xaccAccountStringToType(const char* str, GNCAccountType *type)
 
3588
{
 
3589
 
 
3590
  GNC_RETURN_ON_MATCH(NONE);
 
3591
  GNC_RETURN_ON_MATCH(BANK);
 
3592
  GNC_RETURN_ON_MATCH(CASH);
 
3593
  GNC_RETURN_ON_MATCH(CREDIT);
 
3594
  GNC_RETURN_ON_MATCH(ASSET);
 
3595
  GNC_RETURN_ON_MATCH(LIABILITY);
 
3596
  GNC_RETURN_ON_MATCH(STOCK);
 
3597
  GNC_RETURN_ON_MATCH(MUTUAL);
 
3598
  GNC_RETURN_ON_MATCH(CURRENCY);
 
3599
  GNC_RETURN_ON_MATCH(INCOME);
 
3600
  GNC_RETURN_ON_MATCH(EXPENSE);
 
3601
  GNC_RETURN_ON_MATCH(EQUITY);
 
3602
  GNC_RETURN_ON_MATCH(RECEIVABLE);
 
3603
  GNC_RETURN_ON_MATCH(PAYABLE);
 
3604
  GNC_RETURN_ON_MATCH(ROOT);
 
3605
  GNC_RETURN_ON_MATCH(CHECKING);
 
3606
  GNC_RETURN_ON_MATCH(SAVINGS);
 
3607
  GNC_RETURN_ON_MATCH(MONEYMRKT);
 
3608
  GNC_RETURN_ON_MATCH(CREDITLINE);
 
3609
 
 
3610
  PERR("asked to translate unknown account type string %s.\n",
 
3611
       str ? str : "(null)");
 
3612
 
 
3613
  return(FALSE);
 
3614
}
 
3615
 
 
3616
#undef GNC_RETURN_ON_MATCH
 
3617
 
 
3618
/* impedance mismatch is a source of loss */
 
3619
GNCAccountType
 
3620
xaccAccountStringToEnum(const char* str) 
 
3621
{
 
3622
  GNCAccountType type;
 
3623
  gboolean rc;
 
3624
  rc = xaccAccountStringToType(str, &type);
 
3625
  if (FALSE == rc) return ACCT_TYPE_INVALID;
 
3626
  return type;
 
3627
}
 
3628
 
 
3629
/********************************************************************\
 
3630
\********************************************************************/
 
3631
 
 
3632
static char *
 
3633
account_type_name[NUM_ACCOUNT_TYPES] = { 
 
3634
  N_("Bank"),
 
3635
  N_("Cash"),
 
3636
  N_("Asset"),
 
3637
  N_("Credit Card"),
 
3638
  N_("Liability"),
 
3639
  N_("Stock"),
 
3640
  N_("Mutual Fund"),
 
3641
  N_("Currency"),
 
3642
  N_("Income"),
 
3643
  N_("Expense"),
 
3644
  N_("Equity"),
 
3645
  N_("A/Receivable"),
 
3646
  N_("A/Payable")
 
3647
  /*
 
3648
    N_("Checking"),
 
3649
    N_("Savings"),
 
3650
    N_("Money Market"),
 
3651
    N_("Credit Line")
 
3652
  */
 
3653
};
 
3654
 
 
3655
const char *
 
3656
xaccAccountGetTypeStr(GNCAccountType type) {
 
3657
  if (type < 0 || NUM_ACCOUNT_TYPES <= type ) return "";
 
3658
  return _(account_type_name [type]);
 
3659
}
 
3660
 
 
3661
GNCAccountType
 
3662
xaccAccountGetTypeFromStr (const gchar *str)
 
3663
{
 
3664
  gint type;
 
3665
 
 
3666
  for (type = 0; type < NUM_ACCOUNT_TYPES; type++)
 
3667
  {
 
3668
    if (!safe_strcmp (str, _(account_type_name [type])))
 
3669
      return type;
 
3670
  }
 
3671
 
 
3672
  PERR("asked to translate unknown account type string %s.\n",
 
3673
       str ? str : "(null)");
 
3674
 
 
3675
  return ACCT_TYPE_INVALID;
 
3676
}
 
3677
 
 
3678
 
 
3679
/********************************************************************\
 
3680
\********************************************************************/
 
3681
 
 
3682
guint32
 
3683
xaccParentAccountTypesCompatibleWith (GNCAccountType type)
 
3684
{
 
3685
  switch (type) {
 
3686
  case ACCT_TYPE_BANK:
 
3687
  case ACCT_TYPE_CASH:
 
3688
  case ACCT_TYPE_ASSET:
 
3689
  case ACCT_TYPE_STOCK:
 
3690
  case ACCT_TYPE_MUTUAL:
 
3691
  case ACCT_TYPE_CURRENCY:
 
3692
  case ACCT_TYPE_CREDIT:
 
3693
  case ACCT_TYPE_LIABILITY:
 
3694
  case ACCT_TYPE_RECEIVABLE:
 
3695
  case ACCT_TYPE_PAYABLE:
 
3696
    return
 
3697
      (1 << ACCT_TYPE_BANK)       |
 
3698
      (1 << ACCT_TYPE_CASH)       |
 
3699
      (1 << ACCT_TYPE_ASSET)      |
 
3700
      (1 << ACCT_TYPE_STOCK)      |
 
3701
      (1 << ACCT_TYPE_MUTUAL)     |
 
3702
      (1 << ACCT_TYPE_CURRENCY)   |
 
3703
      (1 << ACCT_TYPE_CREDIT)     |
 
3704
      (1 << ACCT_TYPE_LIABILITY)  |
 
3705
      (1 << ACCT_TYPE_RECEIVABLE) |
 
3706
      (1 << ACCT_TYPE_PAYABLE)    |
 
3707
      (1 << ACCT_TYPE_ROOT);
 
3708
  case ACCT_TYPE_INCOME:
 
3709
  case ACCT_TYPE_EXPENSE:
 
3710
    return
 
3711
      (1 << ACCT_TYPE_INCOME)     |
 
3712
      (1 << ACCT_TYPE_EXPENSE)    |
 
3713
      (1 << ACCT_TYPE_ROOT);
 
3714
  case ACCT_TYPE_EQUITY:
 
3715
    return
 
3716
      (1 << ACCT_TYPE_EQUITY)     |
 
3717
      (1 << ACCT_TYPE_ROOT);
 
3718
  default:
 
3719
    PERR("bad account type: %d", type);
 
3720
    return 0;
 
3721
  }
 
3722
}
 
3723
 
 
3724
gboolean
 
3725
xaccAccountTypesCompatible (GNCAccountType parent_type,
 
3726
                            GNCAccountType child_type)
 
3727
{
 
3728
  return ((xaccParentAccountTypesCompatibleWith (parent_type) &
 
3729
           (1 << child_type))
 
3730
          != 0);
 
3731
}
 
3732
 
 
3733
guint32
 
3734
xaccAccountTypesValid(void)
 
3735
{
 
3736
    guint32 mask = (1 << NUM_ACCOUNT_TYPES) - 1;
 
3737
    mask &= ~((1 << ACCT_TYPE_CURRENCY) |  /* DEPRECATED */
 
3738
              (1 << ACCT_TYPE_ROOT));      /* ROOT */
 
3739
 
 
3740
    return mask;
 
3741
}
 
3742
 
 
3743
gboolean
 
3744
xaccAccountIsPriced(const Account *acc)
 
3745
{
 
3746
    AccountPrivate *priv;
 
3747
 
 
3748
    g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
 
3749
 
 
3750
    priv = GET_PRIVATE(acc);
 
3751
    return (priv->type == ACCT_TYPE_STOCK || priv->type == ACCT_TYPE_MUTUAL || 
 
3752
            priv->type == ACCT_TYPE_CURRENCY);
 
3753
}
 
3754
 
 
3755
/********************************************************************\
 
3756
\********************************************************************/
 
3757
 
 
3758
gboolean
 
3759
xaccAccountGetReconcileLastDate (const Account *acc, time_t *last_date)
 
3760
{
 
3761
  KvpValue *v;
 
3762
 
 
3763
  if (!acc) return FALSE;
 
3764
 
 
3765
  v = kvp_frame_get_value(acc->inst.kvp_data, "reconcile-info/last-date");
 
3766
  
 
3767
  if (!v || kvp_value_get_type(v) != KVP_TYPE_GINT64)
 
3768
      return FALSE;
 
3769
 
 
3770
  if (last_date)
 
3771
      *last_date = kvp_value_get_gint64(v);
 
3772
 
 
3773
  return TRUE;
 
3774
}
 
3775
 
 
3776
/********************************************************************\
 
3777
\********************************************************************/
 
3778
 
 
3779
void
 
3780
xaccAccountSetReconcileLastDate (Account *acc, time_t last_date)
 
3781
{
 
3782
  if (!acc) return;
 
3783
 
 
3784
  xaccAccountBeginEdit (acc);
 
3785
  kvp_frame_set_gint64 (acc->inst.kvp_data, 
 
3786
                        "/reconcile-info/last-date", last_date);
 
3787
  mark_account (acc);
 
3788
  xaccAccountCommitEdit (acc);
 
3789
}
 
3790
 
 
3791
/********************************************************************\
 
3792
\********************************************************************/
 
3793
 
 
3794
gboolean
 
3795
xaccAccountGetReconcileLastInterval (const Account *acc, 
 
3796
                                     int *months, int *days)
 
3797
{
 
3798
  KvpValue *v1, *v2;
 
3799
 
 
3800
  if (!acc) return FALSE;
 
3801
 
 
3802
  v1 = kvp_frame_get_value(acc->inst.kvp_data, 
 
3803
                           "reconcile-info/last-interval/months");
 
3804
  v2 = kvp_frame_get_value(acc->inst.kvp_data, 
 
3805
                           "reconcile-info/last-interval/days");
 
3806
  if (!v1 || (kvp_value_get_type (v1) != KVP_TYPE_GINT64) ||
 
3807
      !v2 || (kvp_value_get_type (v2) != KVP_TYPE_GINT64))
 
3808
    return FALSE;
 
3809
 
 
3810
  if (months)
 
3811
    *months = kvp_value_get_gint64 (v1);
 
3812
  if (days)
 
3813
    *days = kvp_value_get_gint64 (v2);
 
3814
  return TRUE;
 
3815
}
 
3816
 
 
3817
/********************************************************************\
 
3818
\********************************************************************/
 
3819
 
 
3820
void
 
3821
xaccAccountSetReconcileLastInterval (Account *acc, int months, int days)
 
3822
{
 
3823
  KvpFrame *frame;
 
3824
  if (!acc) return;
 
3825
 
 
3826
  xaccAccountBeginEdit (acc);
 
3827
 
 
3828
  frame = kvp_frame_get_frame_slash (acc->inst.kvp_data, 
 
3829
         "/reconcile-info/last-interval");
 
3830
  g_assert(frame);
 
3831
 
 
3832
  kvp_frame_set_gint64 (frame, "months", months);
 
3833
  kvp_frame_set_gint64 (frame, "days", days);
 
3834
 
 
3835
  mark_account (acc);
 
3836
  xaccAccountCommitEdit (acc);
 
3837
}
 
3838
 
 
3839
/********************************************************************\
 
3840
\********************************************************************/
 
3841
 
 
3842
gboolean
 
3843
xaccAccountGetReconcilePostponeDate (const Account *acc, time_t *postpone_date)
 
3844
{
 
3845
  KvpValue *v;
 
3846
 
 
3847
  if (!acc) return FALSE;
 
3848
 
 
3849
  v = kvp_frame_get_value(acc->inst.kvp_data, "reconcile-info/postpone/date");
 
3850
  if (!v || kvp_value_get_type (v) != KVP_TYPE_GINT64)
 
3851
      return FALSE;
 
3852
 
 
3853
  if (postpone_date)
 
3854
      *postpone_date = kvp_value_get_gint64 (v);
 
3855
 
 
3856
  return TRUE;
 
3857
}
 
3858
 
 
3859
/********************************************************************\
 
3860
\********************************************************************/
 
3861
 
 
3862
void
 
3863
xaccAccountSetReconcilePostponeDate (Account *acc, time_t postpone_date)
 
3864
{
 
3865
  if (!acc) return;
 
3866
 
 
3867
  xaccAccountBeginEdit (acc);
 
3868
 
 
3869
  /* XXX this should be using timespecs, not gints !! */
 
3870
  kvp_frame_set_gint64 (acc->inst.kvp_data,
 
3871
            "reconcile-info/postpone/date", postpone_date);
 
3872
  mark_account (acc);
 
3873
  xaccAccountCommitEdit (acc);
 
3874
}
 
3875
 
 
3876
/********************************************************************\
 
3877
\********************************************************************/
 
3878
 
 
3879
gboolean
 
3880
xaccAccountGetReconcilePostponeBalance (const Account *acc, 
 
3881
                                        gnc_numeric *balance)
 
3882
{
 
3883
  KvpValue *v;
 
3884
 
 
3885
  if (!acc) return FALSE;
 
3886
 
 
3887
  v = kvp_frame_get_value(acc->inst.kvp_data, 
 
3888
                          "reconcile-info/postpone/balance");
 
3889
  if (!v || kvp_value_get_type (v) != KVP_TYPE_NUMERIC)
 
3890
      return FALSE;
 
3891
 
 
3892
  if (balance)
 
3893
      *balance = kvp_value_get_numeric (v);
 
3894
 
 
3895
  return TRUE;
 
3896
}
 
3897
 
 
3898
/********************************************************************\
 
3899
\********************************************************************/
 
3900
 
 
3901
void
 
3902
xaccAccountSetReconcilePostponeBalance (Account *acc, gnc_numeric balance)
 
3903
{
 
3904
  if (!acc) return;
 
3905
 
 
3906
  xaccAccountBeginEdit (acc);
 
3907
  kvp_frame_set_gnc_numeric (acc->inst.kvp_data,
 
3908
           "/reconcile-info/postpone/balance", balance);
 
3909
  mark_account (acc);
 
3910
  xaccAccountCommitEdit (acc);
 
3911
}
 
3912
 
 
3913
/********************************************************************\
 
3914
 
 
3915
\********************************************************************/
 
3916
 
 
3917
void
 
3918
xaccAccountClearReconcilePostpone (Account *acc)
 
3919
{
 
3920
  if (!acc) return;
 
3921
 
 
3922
  xaccAccountBeginEdit (acc);
 
3923
  kvp_frame_set_value (acc->inst.kvp_data, "reconcile-info/postpone", NULL);
 
3924
  mark_account (acc);
 
3925
  xaccAccountCommitEdit (acc);
 
3926
}
 
3927
 
 
3928
/********************************************************************\
 
3929
\********************************************************************/
 
3930
 
 
3931
/* xaccAccountGetAutoInterestXfer: determine whether the auto interest
 
3932
 * xfer option is enabled for this account, and return that value.
 
3933
 * If it is not defined for the account, return the default value.
 
3934
 */
 
3935
gboolean
 
3936
xaccAccountGetAutoInterestXfer (const Account *acc, gboolean default_value)
 
3937
{
 
3938
  const char *str = NULL;
 
3939
  if (!acc) return default_value;
 
3940
 
 
3941
  str = kvp_frame_get_string(acc->inst.kvp_data, 
 
3942
                             "reconcile-info/auto-interest-transfer");
 
3943
  return str ? !strcmp(str, "true") : default_value;
 
3944
}
 
3945
 
 
3946
/********************************************************************\
 
3947
\********************************************************************/
 
3948
 
 
3949
void
 
3950
xaccAccountSetAutoInterestXfer (Account *acc, gboolean option)
 
3951
{
 
3952
  if (!acc) return;
 
3953
 
 
3954
  xaccAccountBeginEdit (acc);
 
3955
  /* FIXME: need KVP_TYPE_BOOLEAN for this someday */
 
3956
  kvp_frame_set_string (acc->inst.kvp_data,
 
3957
                        "/reconcile-info/auto-interest-transfer",
 
3958
                        (option ? "true" : "false"));
 
3959
  mark_account (acc);
 
3960
  xaccAccountCommitEdit (acc);
 
3961
}
 
3962
 
 
3963
/********************************************************************\
 
3964
\********************************************************************/
 
3965
 
 
3966
const char *
 
3967
xaccAccountGetLastNum (const Account *acc)
 
3968
{
 
3969
  return acc ? kvp_frame_get_string(acc->inst.kvp_data, "last-num") : NULL;
 
3970
}
 
3971
 
 
3972
/********************************************************************\
 
3973
\********************************************************************/
 
3974
 
 
3975
void
 
3976
xaccAccountSetLastNum (Account *acc, const char *num)
 
3977
{
 
3978
  if (!acc) return;
 
3979
 
 
3980
  xaccAccountBeginEdit (acc);
 
3981
  kvp_frame_set_string(acc->inst.kvp_data, "last-num", num);
 
3982
  mark_account (acc);
 
3983
  xaccAccountCommitEdit (acc);
 
3984
}
 
3985
 
 
3986
/********************************************************************\
 
3987
\********************************************************************/
 
3988
 
 
3989
void
 
3990
dxaccAccountSetPriceSrc(Account *acc, const char *src)
 
3991
{
 
3992
  if (!acc) return;
 
3993
 
 
3994
  xaccAccountBeginEdit(acc);
 
3995
  if (xaccAccountIsPriced(acc)) {
 
3996
      kvp_frame_set_slot_nc(acc->inst.kvp_data,
 
3997
                            "old-price-source",
 
3998
                            src ? kvp_value_new_string(src) : NULL);
 
3999
      mark_account (acc);
 
4000
  }
 
4001
  
 
4002
  qof_instance_set_dirty(&acc->inst);
 
4003
  xaccAccountCommitEdit(acc);
 
4004
}
 
4005
 
 
4006
/********************************************************************\
 
4007
\********************************************************************/
 
4008
 
 
4009
const char*
 
4010
dxaccAccountGetPriceSrc(const Account *acc) 
 
4011
{
 
4012
  if(!acc) return NULL;
 
4013
 
 
4014
  if (xaccAccountIsPriced(acc)) {
 
4015
      KvpValue *value = kvp_frame_get_slot(acc->inst.kvp_data, 
 
4016
                                           "old-price-source");
 
4017
      if (value) return (kvp_value_get_string(value));
 
4018
  }
 
4019
  return NULL;
 
4020
}
 
4021
 
 
4022
/********************************************************************\
 
4023
\********************************************************************/
 
4024
 
 
4025
void
 
4026
dxaccAccountSetQuoteTZ(Account *acc, const char *tz) 
 
4027
{
 
4028
  if (!acc) return;
 
4029
 
 
4030
  xaccAccountBeginEdit(acc);
 
4031
  if (xaccAccountIsPriced(acc)) {
 
4032
      kvp_frame_set_slot_nc(acc->inst.kvp_data,
 
4033
                            "old-quote-tz",
 
4034
                            tz ? kvp_value_new_string(tz) : NULL);
 
4035
      mark_account (acc);
 
4036
  }
 
4037
  qof_instance_set_dirty(&acc->inst);
 
4038
  xaccAccountCommitEdit(acc);
 
4039
}
 
4040
 
 
4041
/********************************************************************\
 
4042
\********************************************************************/
 
4043
 
 
4044
const char*
 
4045
dxaccAccountGetQuoteTZ(const Account *acc) 
 
4046
{
 
4047
  if (!acc) return NULL;
 
4048
 
 
4049
  if (xaccAccountIsPriced(acc)) {
 
4050
      KvpValue *value = kvp_frame_get_slot(acc->inst.kvp_data, "old-quote-tz");
 
4051
      if(value) return (kvp_value_get_string(value));
 
4052
  }
 
4053
  return NULL;
 
4054
}
 
4055
 
 
4056
/********************************************************************\
 
4057
\********************************************************************/
 
4058
 
 
4059
void
 
4060
xaccAccountSetReconcileChildrenStatus(Account *acc, gboolean status)
 
4061
 
4062
  if (!acc) return;
 
4063
  
 
4064
  xaccAccountBeginEdit (acc);
 
4065
  
 
4066
  /* XXX FIXME: someday this should use KVP_TYPE_BOOLEAN */
 
4067
  kvp_frame_set_gint64 (acc->inst.kvp_data, 
 
4068
                        "/reconcile-info/include-children", status);
 
4069
  mark_account(acc);
 
4070
  xaccAccountCommitEdit (acc);
 
4071
}
 
4072
 
 
4073
/********************************************************************\
 
4074
\********************************************************************/
 
4075
 
 
4076
gboolean
 
4077
xaccAccountGetReconcileChildrenStatus(const Account *acc)
 
4078
{
 
4079
  /* access the account's kvp-data for status and return that, if no value
 
4080
   * is found then we can assume not to include the children, that being
 
4081
   * the default behaviour 
 
4082
   */
 
4083
  return acc ? kvp_frame_get_gint64(acc->inst.kvp_data, 
 
4084
                                    "reconcile-info/include-children") : FALSE;
 
4085
}
 
4086
 
 
4087
/********************************************************************\
 
4088
\********************************************************************/
 
4089
 
 
4090
/* The caller of this function can get back one or both of the
 
4091
 * matching split and transaction pointers, depending on whether
 
4092
 * a valid pointer to the location to store those pointers is
 
4093
 * passed.
 
4094
 */
 
4095
static void
 
4096
finder_help_function(const Account *acc, const char *description,
 
4097
                     Split **split, Transaction **trans )
 
4098
{
 
4099
  AccountPrivate *priv;
 
4100
  GList *slp;
 
4101
 
 
4102
  /* First, make sure we set the data to NULL BEFORE we start */
 
4103
  if (split) *split = NULL;
 
4104
  if (trans) *trans = NULL;
 
4105
 
 
4106
  /* Then see if we have any work to do */
 
4107
  if (acc == NULL) return;
 
4108
 
 
4109
  /* Why is this loop iterated backwards ?? Presumably because the split
 
4110
   * list is in date order, and the most recent matches should be 
 
4111
   * returned!?  */
 
4112
  priv = GET_PRIVATE(acc);
 
4113
  for (slp = g_list_last(priv->splits); slp; slp = slp->prev) {
 
4114
    Split *lsplit = slp->data;
 
4115
    Transaction *ltrans = xaccSplitGetParent(lsplit);
 
4116
 
 
4117
    if (safe_strcmp (description, xaccTransGetDescription (ltrans)) == 0)
 
4118
    {
 
4119
      if (split) *split = lsplit;
 
4120
      if (trans) *trans = ltrans;
 
4121
      return;
 
4122
    }
 
4123
  }
 
4124
}
 
4125
 
 
4126
Split *
 
4127
xaccAccountFindSplitByDesc(const Account *acc, const char *description)
 
4128
{
 
4129
  Split *split;
 
4130
 
 
4131
  /* Get the split which has a transaction matching the description. */
 
4132
  finder_help_function(acc, description, &split, NULL);
 
4133
  return split;
 
4134
}
 
4135
 
 
4136
/* This routine is for finding a matching transaction in an account by
 
4137
 * matching on the description field. [CAS: The rest of this comment
 
4138
 * seems to belong somewhere else.] This routine is used for
 
4139
 * auto-filling in registers with a default leading account. The
 
4140
 * dest_trans is a transaction used for currency checking. */
 
4141
Transaction *
 
4142
xaccAccountFindTransByDesc(const Account *acc, const char *description)
 
4143
{
 
4144
  Transaction *trans;
 
4145
 
 
4146
  /* Get the transation matching the description. */
 
4147
  finder_help_function(acc, description, NULL, &trans);
 
4148
  return trans;
 
4149
}
 
4150
 
 
4151
/* ================================================================ */
 
4152
/* Concatenation, Mergeing functions                                */
 
4153
 
 
4154
void
 
4155
gnc_account_join_children (Account *to_parent, Account *from_parent)
 
4156
{
 
4157
  AccountPrivate *from_priv;
 
4158
  GList *children, *node;
 
4159
 
 
4160
  /* errors */
 
4161
  g_return_if_fail(GNC_IS_ACCOUNT(to_parent));
 
4162
  g_return_if_fail(GNC_IS_ACCOUNT(from_parent));
 
4163
 
 
4164
  /* optimizations */
 
4165
  from_priv = GET_PRIVATE(from_parent);
 
4166
  if (!from_priv->children)
 
4167
      return;
 
4168
 
 
4169
  ENTER (" ");
 
4170
  children = g_list_copy(from_priv->children);
 
4171
  for (node = children; node; node = g_list_next(node))
 
4172
    gnc_account_append_child(to_parent, node->data);
 
4173
  g_list_free(children);
 
4174
  LEAVE (" ");
 
4175
}
 
4176
 
 
4177
void
 
4178
gnc_account_copy_children (Account *to, Account *from)
 
4179
{
 
4180
   AccountPrivate *to_priv, *from_priv;
 
4181
   GList *node;
 
4182
   QofBook *to_book;
 
4183
 
 
4184
   /* errors */
 
4185
   g_return_if_fail(GNC_IS_ACCOUNT(to));
 
4186
   g_return_if_fail(GNC_IS_ACCOUNT(from));
 
4187
 
 
4188
   /* optimizations */
 
4189
   to_priv = GET_PRIVATE(to);
 
4190
   from_priv = GET_PRIVATE(from);
 
4191
   if (!from_priv->children)
 
4192
       return;
 
4193
 
 
4194
   to_book = gnc_account_get_book(to);
 
4195
   if (!to_book) return;
 
4196
 
 
4197
   ENTER (" ");
 
4198
   xaccAccountBeginEdit(to);
 
4199
   xaccAccountBeginEdit(from);
 
4200
   for (node = from_priv->children; node; node=node->next)
 
4201
   {
 
4202
      Account *to_acc, *from_acc = node->data;
 
4203
 
 
4204
      /* This will copy the basic data and the KVP.  It will
 
4205
       * not copy any splits/transactions. It will gemini. */
 
4206
      to_acc = xaccCloneAccount (from_acc, to_book);
 
4207
 
 
4208
      xaccAccountBeginEdit (to_acc);
 
4209
      to_priv->children = g_list_append(to_priv->children, to_acc);
 
4210
 
 
4211
      GET_PRIVATE(to_acc)->parent = to;
 
4212
      qof_instance_set_dirty(&to_acc->inst);
 
4213
 
 
4214
      /* Copy child accounts too. */
 
4215
      if (GET_PRIVATE(from_acc)->children)
 
4216
      {
 
4217
        gnc_account_copy_children(to_acc, from_acc);
 
4218
      }
 
4219
      xaccAccountCommitEdit (to_acc);
 
4220
      qof_event_gen (&to_acc->inst, QOF_EVENT_CREATE, NULL);
 
4221
      /* DRH - Should this send ADD/REMOVE events */
 
4222
   }
 
4223
   xaccAccountCommitEdit(from);
 
4224
   xaccAccountCommitEdit(to);
 
4225
   LEAVE (" ");
 
4226
}
 
4227
 
 
4228
/********************************************************************\
 
4229
\********************************************************************/
 
4230
 
 
4231
void 
 
4232
gnc_account_merge_children (Account *parent)
 
4233
{
 
4234
  AccountPrivate *ppriv, *priv_a, *priv_b;
 
4235
  GList *node_a, *node_b, *work, *worker;
 
4236
 
 
4237
  g_return_if_fail(GNC_IS_ACCOUNT(parent));
 
4238
 
 
4239
  ppriv = GET_PRIVATE(parent);
 
4240
  for (node_a = ppriv->children; node_a; node_a = node_a->next)
 
4241
  {
 
4242
    Account *acc_a = node_a->data;
 
4243
 
 
4244
    priv_a = GET_PRIVATE(acc_a);
 
4245
    for (node_b = node_a->next; node_b; node_b = g_list_next(node_b))
 
4246
    {
 
4247
      Account *acc_b = node_b->data;
 
4248
 
 
4249
      priv_b = GET_PRIVATE(acc_b);
 
4250
      if (0 != safe_strcmp(priv_a->accountName, priv_b->accountName))
 
4251
        continue;
 
4252
      if (0 != safe_strcmp(priv_a->accountCode, priv_b->accountCode))
 
4253
        continue;
 
4254
      if (0 != safe_strcmp(priv_a->description, priv_b->description))
 
4255
        continue;
 
4256
      if (!gnc_commodity_equiv(priv_a->commodity, priv_b->commodity))
 
4257
        continue;
 
4258
      if (0 != safe_strcmp(xaccAccountGetNotes(acc_a),
 
4259
                           xaccAccountGetNotes(acc_b)))
 
4260
        continue;
 
4261
      if (priv_a->type != priv_b->type)
 
4262
        continue;
 
4263
 
 
4264
      /* consolidate children */
 
4265
      if (priv_b->children) {
 
4266
        work = g_list_copy(priv_b->children);
 
4267
        for (worker = work; worker; worker = g_list_next(worker))
 
4268
          gnc_account_append_child (acc_a, (Account *)worker->data);
 
4269
        g_list_free(work);
 
4270
 
 
4271
        qof_event_gen (&acc_a->inst, QOF_EVENT_MODIFY, NULL);
 
4272
        qof_event_gen (&acc_b->inst, QOF_EVENT_MODIFY, NULL);
 
4273
      }
 
4274
 
 
4275
      /* recurse to do the children's children */
 
4276
      gnc_account_merge_children (acc_a);
 
4277
 
 
4278
      /* consolidate transactions */
 
4279
      while (priv_b->splits)
 
4280
        xaccSplitSetAccount (priv_b->splits->data, acc_a);
 
4281
 
 
4282
      /* move back one before removal. next iteration around the loop
 
4283
       * will get the node after node_b */
 
4284
      node_b = g_list_previous(node_b);
 
4285
 
 
4286
      /* The destroy function will remove from list -- node_a is ok,
 
4287
       * it's before node_b */
 
4288
      xaccAccountBeginEdit (acc_b);
 
4289
      xaccAccountDestroy (acc_b);
 
4290
    }
 
4291
  }
 
4292
}
 
4293
 
 
4294
/* ================================================================ */
 
4295
/* Transaction Traversal functions                                  */
 
4296
 
 
4297
 
 
4298
void
 
4299
xaccSplitsBeginStagedTransactionTraversals (GList *splits)
 
4300
{
 
4301
  GList *lp;
 
4302
 
 
4303
  for (lp = splits; lp; lp = lp->next)
 
4304
  {
 
4305
    Split *s = lp->data;
 
4306
    Transaction *trans = s->parent;
 
4307
 
 
4308
    if (trans)
 
4309
      trans->marker = 0;
 
4310
  }
 
4311
}
 
4312
 
 
4313
/* original function */
 
4314
void
 
4315
xaccAccountBeginStagedTransactionTraversals (const Account *account)
 
4316
{
 
4317
    AccountPrivate *priv;
 
4318
 
 
4319
    if (!account)
 
4320
        return;
 
4321
    priv = GET_PRIVATE(account);
 
4322
    xaccSplitsBeginStagedTransactionTraversals(priv->splits);
 
4323
}
 
4324
 
 
4325
gboolean
 
4326
xaccTransactionTraverse (Transaction *trans, int stage)
 
4327
{
 
4328
  if (trans == NULL) return FALSE;
 
4329
 
 
4330
  if (trans->marker < stage)
 
4331
  {
 
4332
    trans->marker = stage;
 
4333
    return TRUE;
 
4334
  }
 
4335
 
 
4336
  return FALSE;
 
4337
}
 
4338
 
 
4339
gboolean
 
4340
xaccSplitTransactionTraverse (Split *split, int stage)
 
4341
{
 
4342
  if (split == NULL) return FALSE;
 
4343
 
 
4344
  return xaccTransactionTraverse (split->parent, stage);
 
4345
}
 
4346
 
 
4347
static void do_one_split (Split *s, gpointer data)
 
4348
{
 
4349
  Transaction *trans = s->parent;
 
4350
  trans->marker = 0;
 
4351
}
 
4352
 
 
4353
static void do_one_account (Account *account, gpointer data)
 
4354
{
 
4355
    AccountPrivate *priv = GET_PRIVATE(account);
 
4356
    g_list_foreach(priv->splits, (GFunc)do_one_split, NULL);
 
4357
}
 
4358
 
 
4359
/* Replacement for xaccGroupBeginStagedTransactionTraversals */
 
4360
void
 
4361
gnc_account_tree_begin_staged_transaction_traversals (Account *account)
 
4362
{
 
4363
  GList *descendants;
 
4364
 
 
4365
  descendants = gnc_account_get_descendants(account);
 
4366
  g_list_foreach(descendants, (GFunc)do_one_account, NULL);
 
4367
  g_list_free(descendants);
 
4368
}
 
4369
 
 
4370
int
 
4371
xaccAccountStagedTransactionTraversal (const Account *acc,
 
4372
                                       unsigned int stage,
 
4373
                                       TransactionCallback thunk,
 
4374
                                       void *cb_data)
 
4375
{
 
4376
  AccountPrivate *priv;
 
4377
  GList *split_p;
 
4378
  Transaction *trans;
 
4379
  Split *s;
 
4380
  int retval;
 
4381
 
 
4382
  if (!acc) return 0;
 
4383
 
 
4384
  priv = GET_PRIVATE(acc);
 
4385
  for(split_p = priv->splits; split_p; split_p = g_list_next(split_p)) {
 
4386
    s = split_p->data;
 
4387
    trans = s->parent;   
 
4388
    if (trans && (trans->marker < stage)) {
 
4389
      trans->marker = stage;
 
4390
      if (thunk) {
 
4391
        retval = thunk(trans, cb_data);
 
4392
        if (retval) return retval;
 
4393
      }
 
4394
    }
 
4395
  }
 
4396
 
 
4397
  return 0;
 
4398
}
 
4399
 
 
4400
int
 
4401
gnc_account_tree_staged_transaction_traversal (const Account *acc,
 
4402
                                               unsigned int stage,
 
4403
                                               TransactionCallback thunk,
 
4404
                                               void *cb_data)
 
4405
{
 
4406
  const AccountPrivate *priv;
 
4407
  GList *acc_p, *split_p;
 
4408
  Transaction *trans;
 
4409
  Split *s;
 
4410
  int retval;
 
4411
 
 
4412
  if (!acc) return 0;
 
4413
 
 
4414
  /* depth first traversal */
 
4415
  priv = GET_PRIVATE(acc);
 
4416
  for (acc_p = priv->children; acc_p; acc_p = g_list_next(acc_p)) {
 
4417
    retval = gnc_account_tree_staged_transaction_traversal(acc_p->data, stage,
 
4418
                                                   thunk, cb_data);
 
4419
    if (retval) return retval;
 
4420
  }
 
4421
 
 
4422
  /* Now this account */
 
4423
  for(split_p = priv->splits; split_p; split_p = g_list_next(split_p)) {
 
4424
    s = split_p->data;
 
4425
    trans = s->parent;   
 
4426
    if (trans && (trans->marker < stage)) {
 
4427
      trans->marker = stage;
 
4428
      if (thunk) {
 
4429
        retval = thunk(trans, cb_data);
 
4430
        if (retval) return retval;
 
4431
      }
 
4432
    }
 
4433
  }
 
4434
 
 
4435
  return 0;
 
4436
}
 
4437
 
 
4438
/********************************************************************\
 
4439
\********************************************************************/
 
4440
 
 
4441
int
 
4442
xaccAccountTreeForEachTransaction (Account *acc,
 
4443
                                   int (*proc)(Transaction *t, void *data),
 
4444
                                   void *data)
 
4445
{
 
4446
  if (!acc || !proc) return 0;
 
4447
 
 
4448
  gnc_account_tree_begin_staged_transaction_traversals (acc);
 
4449
  return gnc_account_tree_staged_transaction_traversal (acc, 42, proc, data);
 
4450
}
 
4451
 
 
4452
 
 
4453
gint
 
4454
xaccAccountForEachTransaction(const Account *acc, TransactionCallback proc,
 
4455
                              void *data) 
 
4456
{
 
4457
  if (!acc || !proc) return 0;
 
4458
  xaccAccountBeginStagedTransactionTraversals (acc);
 
4459
  return xaccAccountStagedTransactionTraversal(acc, 42, proc, data);
 
4460
}
 
4461
 
 
4462
/* ================================================================ */
 
4463
/* QofObject function implementation and registration */
 
4464
 
 
4465
static QofObject account_object_def = {
 
4466
  interface_version:     QOF_OBJECT_VERSION,
 
4467
  e_type:                GNC_ID_ACCOUNT,
 
4468
  type_label:            "Account",
 
4469
  create:                (gpointer)xaccMallocAccount,
 
4470
  book_begin:            NULL,
 
4471
  book_end:              NULL,
 
4472
  is_dirty:              qof_collection_is_dirty,
 
4473
  mark_clean:            qof_collection_mark_clean,
 
4474
  foreach:               qof_collection_foreach,
 
4475
  printable:             (const char* (*)(gpointer)) xaccAccountGetName,
 
4476
  version_cmp:           (int (*)(gpointer,gpointer)) qof_instance_version_cmp,
 
4477
};
 
4478
 
 
4479
gboolean xaccAccountRegister (void)
 
4480
{
 
4481
  static QofParam params[] = {
 
4482
    { ACCOUNT_NAME_, QOF_TYPE_STRING, 
 
4483
      (QofAccessFunc) xaccAccountGetName,
 
4484
      (QofSetterFunc) xaccAccountSetName },
 
4485
    { ACCOUNT_CODE_, QOF_TYPE_STRING, 
 
4486
      (QofAccessFunc) xaccAccountGetCode,
 
4487
      (QofSetterFunc) xaccAccountSetCode },
 
4488
    { ACCOUNT_DESCRIPTION_, QOF_TYPE_STRING, 
 
4489
      (QofAccessFunc) xaccAccountGetDescription,
 
4490
      (QofSetterFunc) xaccAccountSetDescription },
 
4491
    { ACCOUNT_NOTES_, QOF_TYPE_STRING, 
 
4492
      (QofAccessFunc) xaccAccountGetNotes,
 
4493
      (QofSetterFunc) xaccAccountSetNotes },
 
4494
    { ACCOUNT_PRESENT_, QOF_TYPE_NUMERIC, 
 
4495
      (QofAccessFunc) xaccAccountGetPresentBalance, NULL },
 
4496
    { ACCOUNT_BALANCE_, QOF_TYPE_NUMERIC, 
 
4497
      (QofAccessFunc) xaccAccountGetBalance, NULL },
 
4498
    { ACCOUNT_CLEARED_, QOF_TYPE_NUMERIC, 
 
4499
      (QofAccessFunc) xaccAccountGetClearedBalance, NULL },
 
4500
    { ACCOUNT_RECONCILED_, QOF_TYPE_NUMERIC, 
 
4501
      (QofAccessFunc) xaccAccountGetReconciledBalance, NULL },
 
4502
    { ACCOUNT_TYPE_, QOF_TYPE_STRING, 
 
4503
      (QofAccessFunc) qofAccountGetTypeString,
 
4504
      (QofSetterFunc) qofAccountSetType },
 
4505
    { ACCOUNT_FUTURE_MINIMUM_, QOF_TYPE_NUMERIC, 
 
4506
      (QofAccessFunc) xaccAccountGetProjectedMinimumBalance, NULL },
 
4507
    { ACCOUNT_TAX_RELATED, QOF_TYPE_BOOLEAN, 
 
4508
      (QofAccessFunc) xaccAccountGetTaxRelated, 
 
4509
      (QofSetterFunc) xaccAccountSetTaxRelated },
 
4510
    { ACCOUNT_SCU, QOF_TYPE_INT32, 
 
4511
      (QofAccessFunc) xaccAccountGetCommoditySCU,
 
4512
      (QofSetterFunc) xaccAccountSetCommoditySCU },
 
4513
    { ACCOUNT_NSCU, QOF_TYPE_BOOLEAN, 
 
4514
      (QofAccessFunc) xaccAccountGetNonStdSCU, 
 
4515
      (QofSetterFunc) xaccAccountSetNonStdSCU },
 
4516
    { ACCOUNT_PARENT, GNC_ID_ACCOUNT,
 
4517
      (QofAccessFunc) gnc_account_get_parent, 
 
4518
      (QofSetterFunc) qofAccountSetParent },
 
4519
    { QOF_PARAM_BOOK, QOF_ID_BOOK, 
 
4520
      (QofAccessFunc) qof_instance_get_book, NULL },
 
4521
    { QOF_PARAM_GUID, QOF_TYPE_GUID, 
 
4522
      (QofAccessFunc) qof_instance_get_guid, NULL },
 
4523
    { ACCOUNT_KVP, QOF_TYPE_KVP, 
 
4524
      (QofAccessFunc) qof_instance_get_slots, NULL },
 
4525
    { NULL },
 
4526
  };
 
4527
 
 
4528
  qof_class_register (GNC_ID_ACCOUNT, (QofSortFunc) qof_xaccAccountOrder, params);
 
4529
 
 
4530
  return qof_object_register (&account_object_def);
 
4531
}
 
4532
 
 
4533
/* ======================= END OF FILE =========================== */
 
4534
 
 
4535
// Local Variables:
 
4536
// mode: c
 
4537
// indent-tabs-mode: nil
 
4538
// c-block-comment-prefix: "* "
 
4539
// eval: (c-add-style "gnc" '("k&r" (c-basic-offset . 4) (c-offsets-alist (case-label . +))) t)
 
4540
// End: