00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "config.h"
00027
00028 #include <glib.h>
00029 #include <glib/gi18n.h>
00030 #include <stdlib.h>
00031 #include <string.h>
00032
00033 #include "AccountP.h"
00034 #include "Split.h"
00035 #include "Transaction.h"
00036 #include "TransactionP.h"
00037 #include "gnc-event.h"
00038 #include "gnc-glib-utils.h"
00039 #include "gnc-lot.h"
00040 #include "gnc-lot-p.h"
00041 #include "gnc-pricedb.h"
00042
00043 #define GNC_ID_ROOT_ACCOUNT "RootAccount"
00044
00045 static QofLogModule log_module = GNC_MOD_ACCOUNT;
00046
00047
00048 static gchar account_separator[8] = ".";
00049 gunichar account_uc_separator = ':';
00050
00051 enum {
00052 LAST_SIGNAL
00053 };
00054
00055 enum {
00056 PROP_0,
00057 PROP_NAME,
00058 PROP_FULL_NAME,
00059 PROP_CODE,
00060 PROP_DESCRIPTION,
00061 PROP_NOTES,
00062 PROP_TYPE,
00063
00064 PROP_COMMODITY,
00065 PROP_COMMODITY_SCU,
00066 PROP_NON_STD_SCU,
00067 PROP_SORT_DIRTY,
00068 PROP_BALANCE_DIRTY,
00069 PROP_START_BALANCE,
00070 PROP_START_CLEARED_BALANCE,
00071 PROP_START_RECONCILED_BALANCE,
00072 PROP_END_BALANCE,
00073 PROP_END_CLEARED_BALANCE,
00074 PROP_END_RECONCILED_BALANCE,
00075
00076 PROP_POLICY,
00077 PROP_MARK,
00078 PROP_TAX_RELATED,
00079 PROP_TAX_CODE,
00080 PROP_TAX_SOURCE,
00081 };
00082
00083 typedef struct AccountPrivate
00084 {
00085
00086
00087
00088
00089 char *accountName;
00090
00091
00092
00093
00094
00095
00096
00097
00098 char *accountCode;
00099
00100
00101
00102
00103
00104 char *description;
00105
00106
00107
00108
00109
00110
00111
00112 GNCAccountType type;
00113
00114
00115
00116
00117
00118
00119 gnc_commodity * commodity;
00120 int commodity_scu;
00121 gboolean non_standard_scu;
00122
00123
00124
00125
00126 Account *parent;
00127 GList *children;
00128
00129
00130 gnc_numeric starting_balance;
00131 gnc_numeric starting_cleared_balance;
00132 gnc_numeric starting_reconciled_balance;
00133
00134
00135 gnc_numeric balance;
00136 gnc_numeric cleared_balance;
00137 gnc_numeric reconciled_balance;
00138
00139 gboolean balance_dirty;
00140
00141 GList *splits;
00142 gboolean sort_dirty;
00143
00144 LotList *lots;
00145 GNCPolicy *policy;
00146
00147
00148
00149
00150 short mark;
00151 } AccountPrivate;
00152
00153 #define GET_PRIVATE(o) \
00154 (G_TYPE_INSTANCE_GET_PRIVATE ((o), GNC_TYPE_ACCOUNT, AccountPrivate))
00155
00156
00157
00158
00159
00160
00161
00162
00163 static void xaccAccountBringUpToDate (Account *acc);
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173 const gchar *
00174 gnc_get_account_separator_string (void)
00175 {
00176 return account_separator;
00177 }
00178
00179 gunichar
00180 gnc_get_account_separator (void)
00181 {
00182 return account_uc_separator;
00183 }
00184
00185 void
00186 gnc_set_account_separator (const gchar *separator)
00187 {
00188 gunichar uc;
00189 gint count;
00190
00191 uc = g_utf8_get_char_validated(separator, -1);
00192 if ((uc == (gunichar)-2) || (uc == (gunichar)-1) || g_unichar_isalnum(uc)) {
00193 account_uc_separator = ':';
00194 strcpy(account_separator, ":");
00195 return;
00196 }
00197
00198 account_uc_separator = uc;
00199 count = g_unichar_to_utf8(uc, account_separator);
00200 account_separator[count] = '\0';
00201 }
00202
00203
00204
00205
00206 G_INLINE_FUNC void mark_account (Account *acc);
00207 void
00208 mark_account (Account *acc)
00209 {
00210 qof_instance_set_dirty(&acc->inst);
00211 }
00212
00213
00214
00215
00216
00217 G_DEFINE_TYPE(Account, gnc_account, QOF_TYPE_INSTANCE)
00218
00219 static void
00220 gnc_account_init(Account* acc)
00221 {
00222 AccountPrivate *priv;
00223
00224 priv = GET_PRIVATE(acc);
00225 priv->parent = NULL;
00226 priv->children = NULL;
00227
00228 priv->accountName = CACHE_INSERT("");
00229 priv->accountCode = CACHE_INSERT("");
00230 priv->description = CACHE_INSERT("");
00231
00232 priv->type = ACCT_TYPE_NONE;
00233
00234 priv->mark = 0;
00235
00236 priv->policy = xaccGetFIFOPolicy();
00237 priv->lots = NULL;
00238
00239 priv->commodity = NULL;
00240 priv->commodity_scu = 0;
00241 priv->non_standard_scu = FALSE;
00242
00243 priv->balance = gnc_numeric_zero();
00244 priv->cleared_balance = gnc_numeric_zero();
00245 priv->reconciled_balance = gnc_numeric_zero();
00246 priv->starting_balance = gnc_numeric_zero();
00247 priv->starting_cleared_balance = gnc_numeric_zero();
00248 priv->starting_reconciled_balance = gnc_numeric_zero();
00249 priv->balance_dirty = FALSE;
00250
00251 priv->splits = NULL;
00252 priv->sort_dirty = FALSE;
00253 }
00254
00255 static void
00256 gnc_account_dispose (GObject *acctp)
00257 {
00258 G_OBJECT_CLASS(gnc_account_parent_class)->dispose(acctp);
00259 }
00260
00261 static void
00262 gnc_account_finalize(GObject* acctp)
00263 {
00264 G_OBJECT_CLASS(gnc_account_parent_class)->finalize(acctp);
00265 }
00266
00267 static void
00268 gnc_account_get_property (GObject *object,
00269 guint prop_id,
00270 GValue *value,
00271 GParamSpec *pspec)
00272 {
00273 Account *account;
00274 AccountPrivate *priv;
00275
00276 g_return_if_fail(GNC_IS_ACCOUNT(object));
00277
00278 account = GNC_ACCOUNT(object);
00279 priv = GET_PRIVATE(account);
00280 switch (prop_id) {
00281 case PROP_NAME:
00282 g_value_set_string(value, priv->accountName);
00283 break;
00284 case PROP_FULL_NAME:
00285 g_value_take_string(value, xaccAccountGetFullName(account));
00286 break;
00287 case PROP_CODE:
00288 g_value_set_string(value, priv->accountCode);
00289 break;
00290 case PROP_DESCRIPTION:
00291 g_value_set_string(value, priv->description);
00292 break;
00293 case PROP_NOTES:
00294 g_value_set_string(value, xaccAccountGetNotes(account));
00295 break;
00296 case PROP_TYPE:
00297
00298 g_value_set_int(value, priv->type);
00299 break;
00300 case PROP_COMMODITY:
00301 g_value_set_object(value, priv->commodity);
00302 break;
00303 case PROP_COMMODITY_SCU:
00304 g_value_set_int(value, priv->commodity_scu);
00305 break;
00306 case PROP_NON_STD_SCU:
00307 g_value_set_boolean(value, priv->non_standard_scu);
00308 break;
00309 case PROP_SORT_DIRTY:
00310 g_value_set_boolean(value, priv->sort_dirty);
00311 break;
00312 case PROP_BALANCE_DIRTY:
00313 g_value_set_boolean(value, priv->balance_dirty);
00314 break;
00315 case PROP_START_BALANCE:
00316 g_value_set_boxed(value, &priv->starting_balance);
00317 break;
00318 case PROP_START_CLEARED_BALANCE:
00319 g_value_set_boxed(value, &priv->starting_cleared_balance);
00320 break;
00321 case PROP_START_RECONCILED_BALANCE:
00322 g_value_set_boxed(value, &priv->starting_reconciled_balance);
00323 break;
00324 case PROP_END_BALANCE:
00325 g_value_set_boxed(value, &priv->balance);
00326 break;
00327 case PROP_END_CLEARED_BALANCE:
00328 g_value_set_boxed(value, &priv->cleared_balance);
00329 break;
00330 case PROP_END_RECONCILED_BALANCE:
00331 g_value_set_boxed(value, &priv->reconciled_balance);
00332 break;
00333 case PROP_POLICY:
00334
00335 g_value_set_pointer(value, priv->policy);
00336 break;
00337 case PROP_MARK:
00338 g_value_set_int(value, priv->mark);
00339 break;
00340 case PROP_TAX_RELATED:
00341 g_value_set_boolean(value, xaccAccountGetTaxRelated(account));
00342 break;
00343 case PROP_TAX_CODE:
00344 g_value_set_string(value, xaccAccountGetTaxUSCode(account));
00345 break;
00346 case PROP_TAX_SOURCE:
00347 g_value_set_string(value,
00348 xaccAccountGetTaxUSPayerNameSource(account));
00349 break;
00350 default:
00351 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
00352 break;
00353 }
00354 }
00355
00356 static void
00357 gnc_account_set_property (GObject *object,
00358 guint prop_id,
00359 const GValue *value,
00360 GParamSpec *pspec)
00361 {
00362 Account *account;
00363 gnc_numeric *number;
00364
00365 g_return_if_fail(GNC_IS_ACCOUNT(object));
00366
00367 account = GNC_ACCOUNT(object);
00368
00369 switch (prop_id) {
00370 case PROP_NAME:
00371 xaccAccountSetName(account, g_value_get_string(value));
00372 break;
00373 case PROP_CODE:
00374 xaccAccountSetCode(account, g_value_get_string(value));
00375 break;
00376 case PROP_DESCRIPTION:
00377 xaccAccountSetDescription(account, g_value_get_string(value));
00378 break;
00379 case PROP_NOTES:
00380 xaccAccountSetNotes(account, g_value_get_string(value));
00381 break;
00382 case PROP_TYPE:
00383
00384 xaccAccountSetType(account, g_value_get_int(value));
00385 break;
00386 case PROP_COMMODITY:
00387 xaccAccountSetCommodity(account, g_value_get_object(value));
00388 break;
00389 case PROP_COMMODITY_SCU:
00390 xaccAccountSetCommoditySCU(account, g_value_get_int(value));
00391 break;
00392 case PROP_SORT_DIRTY:
00393 gnc_account_set_sort_dirty(account);
00394 break;
00395 case PROP_BALANCE_DIRTY:
00396 gnc_account_set_balance_dirty(account);
00397 break;
00398 case PROP_START_BALANCE:
00399 number = g_value_get_boxed(value);
00400 gnc_account_set_start_balance(account, *number);
00401 break;
00402 case PROP_START_CLEARED_BALANCE:
00403 number = g_value_get_boxed(value);
00404 gnc_account_set_start_cleared_balance(account, *number);
00405 break;
00406 case PROP_START_RECONCILED_BALANCE:
00407 number = g_value_get_boxed(value);
00408 gnc_account_set_start_reconciled_balance(account, *number);
00409 break;
00410 case PROP_POLICY:
00411 gnc_account_set_policy(account, g_value_get_pointer(value));
00412 break;
00413 case PROP_MARK:
00414 xaccAccountSetMark(account, g_value_get_int(value));
00415 break;
00416 case PROP_TAX_RELATED:
00417 xaccAccountSetTaxRelated(account, g_value_get_boolean(value));
00418 break;
00419 case PROP_TAX_CODE:
00420 xaccAccountSetTaxUSCode(account, g_value_get_string(value));
00421 break;
00422 case PROP_TAX_SOURCE:
00423 xaccAccountSetTaxUSPayerNameSource(account,
00424 g_value_get_string(value));
00425 default:
00426 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
00427 break;
00428 }
00429 }
00430
00431
00432
00433 static void
00434 gnc_account_class_init (AccountClass *klass)
00435 {
00436 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
00437
00438 gobject_class->dispose = gnc_account_dispose;
00439 gobject_class->finalize = gnc_account_finalize;
00440 gobject_class->set_property = gnc_account_set_property;
00441 gobject_class->get_property = gnc_account_get_property;
00442
00443 g_type_class_add_private(klass, sizeof(AccountPrivate));
00444
00445 g_object_class_install_property
00446 (gobject_class,
00447 PROP_NAME,
00448 g_param_spec_string ("name",
00449 "Account Name",
00450 "The accountName is an arbitrary string "
00451 "assigned by the user. It is intended to "
00452 "a short, 5 to 30 character long string "
00453 "that is displayed by the GUI as the "
00454 "account mnemonic. Account names may be "
00455 "repeasted. but no two accounts that share "
00456 "a parent may have the same name.",
00457 NULL,
00458 G_PARAM_READWRITE));
00459
00460 g_object_class_install_property
00461 (gobject_class,
00462 PROP_FULL_NAME,
00463 g_param_spec_string ("fullname",
00464 "Full Account Name",
00465 "The name of the account concatenated with "
00466 "all its parent account names to indicate "
00467 "a unique account.",
00468 NULL,
00469 G_PARAM_READABLE));
00470
00471 g_object_class_install_property
00472 (gobject_class,
00473 PROP_CODE,
00474 g_param_spec_string ("code",
00475 "Account Code",
00476 "The account code is an arbitrary string "
00477 "assigned by the user. It is intended to "
00478 "be reporting code that is a synonym for "
00479 "the accountName.",
00480 NULL,
00481 G_PARAM_READWRITE));
00482
00483 g_object_class_install_property
00484 (gobject_class,
00485 PROP_DESCRIPTION,
00486 g_param_spec_string ("description",
00487 "Account Description",
00488 "The account description is an arbitrary "
00489 "string assigned by the user. It is intended "
00490 "to be a longer, 1-5 sentence description of "
00491 "what this account is all about.",
00492 NULL,
00493 G_PARAM_READWRITE));
00494
00495 g_object_class_install_property
00496 (gobject_class,
00497 PROP_NOTES,
00498 g_param_spec_string ("notes",
00499 "Account Notes",
00500 "The account notes is an arbitrary provided "
00501 "for the user to attach any orther text that "
00502 "they would like to associate with the account.",
00503 NULL,
00504 G_PARAM_READWRITE));
00505
00506 g_object_class_install_property
00507 (gobject_class,
00508 PROP_TYPE,
00509 g_param_spec_int ("type",
00510 "Account Type",
00511 "The account type, picked from the enumerated list "
00512 "that includes ACCT_TYPE_BANK, ACCT_TYPE_STOCK, "
00513 "ACCT_TYPE_CREDIT, ACCT_TYPE_INCOME, etc.",
00514 ACCT_TYPE_NONE,
00515 NUM_ACCOUNT_TYPES - 1,
00516 ACCT_TYPE_BANK,
00517 G_PARAM_READWRITE));
00518
00519 g_object_class_install_property
00520 (gobject_class,
00521 PROP_COMMODITY,
00522 g_param_spec_object ("commodity",
00523 "Commodity",
00524 "The commodity field denotes the kind of "
00525 "'stuff' stored in this account, whether "
00526 "it is USD, gold, stock, etc.",
00527 GNC_TYPE_COMMODITY,
00528 G_PARAM_READWRITE));
00529
00530 g_object_class_install_property
00531 (gobject_class,
00532 PROP_COMMODITY_SCU,
00533 g_param_spec_int ("commodity-scu",
00534 "Commodity SCU",
00535 "The smallest fraction of the commodity that is "
00536 "tracked. This number is used as the denominator "
00537 "value in 1/x, so a value of 100 says that the "
00538 "commodity can be divided into hundreths. E.G."
00539 "1 USD can be divided into 100 cents.",
00540 0,
00541 G_MAXINT32,
00542 1000000,
00543 G_PARAM_READWRITE));
00544
00545 g_object_class_install_property
00546 (gobject_class,
00547 PROP_NON_STD_SCU,
00548 g_param_spec_boolean ("non-std-scu",
00549 "Non-std SCU",
00550 "TRUE id the account SCU doesn't match "
00551 "the commodity SCU. This indicates a case "
00552 "where the two were accidentally set to "
00553 "mismatched values in older versions of "
00554 "GnuCash.",
00555 FALSE,
00556 G_PARAM_READWRITE));
00557
00558 g_object_class_install_property
00559 (gobject_class,
00560 PROP_SORT_DIRTY,
00561 g_param_spec_boolean("sort-dirty",
00562 "Sort Dirty",
00563 "TRUE if the splits in the account needs to be "
00564 "resorted. This flag is set by the accounts "
00565 "code for certain internal modifications, or "
00566 "when external code calls the engine to say a "
00567 "split has been modified in a way that may "
00568 "affect the sort order of the account. Note: "
00569 "This value can only be set to TRUE.",
00570 FALSE,
00571 G_PARAM_READWRITE));
00572
00573 g_object_class_install_property
00574 (gobject_class,
00575 PROP_BALANCE_DIRTY,
00576 g_param_spec_boolean("balance-dirty",
00577 "Balance Dirty",
00578 "TRUE if the running balances in the account "
00579 "needs to be recalculated. This flag is set "
00580 "by the accounts code for certain internal "
00581 "modifications, or when external code calls "
00582 "the engine to say a split has been modified. "
00583 "Note: This value can only be set to TRUE.",
00584 FALSE,
00585 G_PARAM_READWRITE));
00586
00587 g_object_class_install_property
00588 (gobject_class,
00589 PROP_START_BALANCE,
00590 g_param_spec_boxed("start-balance",
00591 "Starting Balance",
00592 "The starting balance for the account. This "
00593 "parameter is intended for use with backends that "
00594 "do not return the complete list of splits for an "
00595 "account, but rather return a partial list. In "
00596 "such a case, the backend will typically return "
00597 "all of the splits after some certain date, and "
00598 "the 'starting balance' will represent the "
00599 "summation of the splits up to that date.",
00600 GNC_TYPE_NUMERIC,
00601 G_PARAM_READWRITE));
00602
00603 g_object_class_install_property
00604 (gobject_class,
00605 PROP_START_CLEARED_BALANCE,
00606 g_param_spec_boxed("start-cleared-balance",
00607 "Starting Cleared Balance",
00608 "The starting cleared balance for the account. "
00609 "This parameter is intended for use with backends "
00610 "that do not return the complete list of splits "
00611 "for an account, but rather return a partial "
00612 "list. In such a case, the backend will "
00613 "typically return all of the splits after "
00614 "some certain date, and the 'starting cleared "
00615 "balance' will represent the summation of the "
00616 "splits up to that date.",
00617 GNC_TYPE_NUMERIC,
00618 G_PARAM_READWRITE));
00619
00620 g_object_class_install_property
00621 (gobject_class,
00622 PROP_START_RECONCILED_BALANCE,
00623 g_param_spec_boxed("start-reconciled-balance",
00624 "Starting Reconciled Balance",
00625 "The starting reconciled balance for the "
00626 "account. This parameter is intended for use "
00627 "with backends that do not return the complete "
00628 "list of splits for an account, but rather return "
00629 "a partial list. In such a case, the backend "
00630 "will typically return all of the splits after "
00631 "some certain date, and the 'starting recontiled "
00632 "balance' will represent the summation of the "
00633 "splits up to that date.",
00634 GNC_TYPE_NUMERIC,
00635 G_PARAM_READWRITE));
00636
00637 g_object_class_install_property
00638 (gobject_class,
00639 PROP_END_BALANCE,
00640 g_param_spec_boxed("end-balance",
00641 "Ending Account Balance",
00642 "This is the current ending balance for the "
00643 "account. It is computed from the sum of the "
00644 "starting balance and all splits in the account.",
00645 GNC_TYPE_NUMERIC,
00646 G_PARAM_READABLE));
00647
00648 g_object_class_install_property
00649 (gobject_class,
00650 PROP_END_CLEARED_BALANCE,
00651 g_param_spec_boxed("end-cleared-balance",
00652 "Ending Account Cleared Balance",
00653 "This is the current ending cleared balance for "
00654 "the account. It is computed from the sum of the "
00655 "starting balance and all cleared splits in the "
00656 "account.",
00657 GNC_TYPE_NUMERIC,
00658 G_PARAM_READABLE));
00659
00660 g_object_class_install_property
00661 (gobject_class,
00662 PROP_END_RECONCILED_BALANCE,
00663 g_param_spec_boxed("end-reconciled-balance",
00664 "Ending Account Reconciled Balance",
00665 "This is the current ending reconciled balance "
00666 "for the account. It is computed from the sum of "
00667 "the starting balance and all reconciled splits "
00668 "in the account.",
00669 GNC_TYPE_NUMERIC,
00670 G_PARAM_READABLE));
00671
00672 g_object_class_install_property
00673 (gobject_class,
00674 PROP_POLICY,
00675 g_param_spec_pointer ("policy",
00676 "Policy",
00677 "The account lots policy.",
00678 G_PARAM_READWRITE));
00679
00680 g_object_class_install_property
00681 (gobject_class,
00682 PROP_MARK,
00683 g_param_spec_int ("acct-mark",
00684 "Account Mark",
00685 "Ipsum Lorem",
00686 0,
00687 G_MAXINT16,
00688 0,
00689 G_PARAM_READWRITE));
00690
00691 g_object_class_install_property
00692 (gobject_class,
00693 PROP_TAX_RELATED,
00694 g_param_spec_boolean ("tax-related",
00695 "Tax Related",
00696 "Whether the account maps to an entry on an "
00697 "income tax document.",
00698 FALSE,
00699 G_PARAM_READWRITE));
00700
00701 g_object_class_install_property
00702 (gobject_class,
00703 PROP_TAX_CODE,
00704 g_param_spec_string ("tax-code",
00705 "Tax Code",
00706 "This is the code for mapping an account to a "
00707 "specific entry on a taxable document. In the "
00708 "United States it is used to transfer totals "
00709 "into tax preparation software.",
00710 NULL,
00711 G_PARAM_READWRITE));
00712
00713 g_object_class_install_property
00714 (gobject_class,
00715 PROP_TAX_SOURCE,
00716 g_param_spec_string ("tax-source",
00717 "Tax Source",
00718 "This is an unknown tax related field.",
00719 NULL,
00720 G_PARAM_READWRITE));
00721 }
00722
00723 static void
00724 xaccInitAccount (Account * acc, QofBook *book)
00725 {
00726 ENTER ("book=%p\n", book);
00727 qof_instance_init_data (&acc->inst, GNC_ID_ACCOUNT, book);
00728
00729 LEAVE ("account=%p\n", acc);
00730 }
00731
00732
00733
00734
00735 QofBook *
00736 gnc_account_get_book(const Account *account)
00737 {
00738 return qof_instance_get_book(QOF_INSTANCE(account));
00739 }
00740
00741
00742
00743
00744 static Account *
00745 gnc_coll_get_root_account (QofCollection *col)
00746 {
00747 if (!col) return NULL;
00748 return qof_collection_get_data (col);
00749 }
00750
00751 static void
00752 gnc_coll_set_root_account (QofCollection *col, Account *root)
00753 {
00754 AccountPrivate *rpriv;
00755 Account *old_root;
00756 if (!col) return;
00757
00758 old_root = gnc_coll_get_root_account (col);
00759 if (old_root == root) return;
00760
00761
00762
00763
00764 rpriv = GET_PRIVATE(root);
00765 if (rpriv->parent) {
00766 xaccAccountBeginEdit(root);
00767 gnc_account_remove_child(rpriv->parent, root);
00768 xaccAccountCommitEdit(root);
00769 }
00770
00771 qof_collection_set_data (col, root);
00772
00773 if (old_root) {
00774 xaccAccountBeginEdit (old_root);
00775 xaccAccountDestroy (old_root);
00776 }
00777 }
00778
00779 Account *
00780 gnc_book_get_root_account (QofBook *book)
00781 {
00782 QofCollection *col;
00783 Account *root;
00784
00785 if (!book) return NULL;
00786 col = qof_book_get_collection (book, GNC_ID_ROOT_ACCOUNT);
00787 root = gnc_coll_get_root_account (col);
00788 if (root == NULL)
00789 root = gnc_account_create_root(book);
00790 return root;
00791 }
00792
00793 void
00794 gnc_book_set_root_account (QofBook *book, Account *root)
00795 {
00796 QofCollection *col;
00797 if (!book) return;
00798
00799 if (root && gnc_account_get_book(root) != book)
00800 {
00801 PERR ("cannot mix and match books freely!");
00802 return;
00803 }
00804
00805 col = qof_book_get_collection (book, GNC_ID_ROOT_ACCOUNT);
00806 gnc_coll_set_root_account (col, root);
00807 }
00808
00809
00810
00811
00812 Account *
00813 xaccMallocAccount (QofBook *book)
00814 {
00815 Account *acc;
00816
00817 g_return_val_if_fail (book, NULL);
00818
00819 acc = g_object_new (GNC_TYPE_ACCOUNT, NULL);
00820 xaccInitAccount (acc, book);
00821 qof_event_gen (&acc->inst, QOF_EVENT_CREATE, NULL);
00822
00823 return acc;
00824 }
00825
00826 Account *
00827 gnc_account_create_root (QofBook *book)
00828 {
00829 Account *root;
00830 AccountPrivate *rpriv;
00831
00832 root = xaccMallocAccount(book);
00833 rpriv = GET_PRIVATE(root);
00834 xaccAccountBeginEdit(root);
00835 rpriv->type = ACCT_TYPE_ROOT;
00836 CACHE_REPLACE(rpriv->accountName, "Root Account");
00837 xaccAccountCommitEdit(root);
00838 gnc_book_set_root_account(book, root);
00839 return root;
00840 }
00841
00842 static Account *
00843 xaccCloneAccountCommon(const Account *from, QofBook *book)
00844 {
00845 Account *ret;
00846 AccountPrivate *from_priv, *priv;
00847
00848 g_return_val_if_fail(GNC_IS_ACCOUNT(from), NULL);
00849 g_return_val_if_fail(QOF_IS_BOOK(book), NULL);
00850
00851 ENTER (" ");
00852 ret = g_object_new (GNC_TYPE_ACCOUNT, NULL);
00853 g_return_val_if_fail (ret, NULL);
00854
00855 from_priv = GET_PRIVATE(from);
00856 priv = GET_PRIVATE(ret);
00857 xaccInitAccount (ret, book);
00858
00859
00860
00861
00862 priv->type = from_priv->type;
00863
00864 priv->accountName = CACHE_INSERT(from_priv->accountName);
00865 priv->accountCode = CACHE_INSERT(from_priv->accountCode);
00866 priv->description = CACHE_INSERT(from_priv->description);
00867
00868 kvp_frame_delete(ret->inst.kvp_data);
00869 ret->inst.kvp_data = kvp_frame_copy(from->inst.kvp_data);
00870
00871
00872
00873 priv->commodity = gnc_commodity_obtain_twin(from_priv->commodity, book);
00874 gnc_commodity_increment_usage_count(priv->commodity);
00875
00876 priv->commodity_scu = from_priv->commodity_scu;
00877 priv->non_standard_scu = from_priv->non_standard_scu;
00878
00879 LEAVE (" ");
00880 return ret;
00881 }
00882
00883 Account *
00884 xaccCloneAccount (const Account *from, QofBook *book)
00885 {
00886 Account *ret = xaccCloneAccountCommon(from, book);
00887 qof_instance_gemini (&ret->inst, (QofInstance *) &from->inst);
00888 g_assert (ret ==
00889 (Account*) qof_instance_lookup_twin (QOF_INSTANCE(from), book));
00890 return ret;
00891 }
00892
00893 Account *
00894 xaccCloneAccountSimple (const Account *from, QofBook *book)
00895 {
00896 Account *ret = xaccCloneAccountCommon(from, book);
00897 qof_instance_set_dirty(&ret->inst);
00898 return ret;
00899 }
00900
00901
00902
00903
00904 static void
00905 xaccFreeOneChildAccount (Account *acc, gpointer dummy)
00906 {
00907
00908
00909 if (qof_instance_get_editlevel(acc) == 0)
00910 xaccAccountBeginEdit(acc);
00911 xaccAccountDestroy(acc);
00912 }
00913
00914 static void
00915 xaccFreeAccountChildren (Account *acc)
00916 {
00917 AccountPrivate *priv;
00918 GList *children;
00919
00920
00921 priv = GET_PRIVATE(acc);
00922 children = g_list_copy(priv->children);
00923 g_list_foreach(children, (GFunc)xaccFreeOneChildAccount, NULL);
00924 g_list_free(children);
00925
00926
00927 if (priv->children)
00928 g_list_free(priv->children);
00929 priv->children = NULL;
00930 }
00931
00932
00933
00934
00935
00936 static void
00937 xaccFreeAccount (Account *acc)
00938 {
00939 AccountPrivate *priv;
00940 GList *lp;
00941
00942 g_return_if_fail(GNC_IS_ACCOUNT(acc));
00943
00944 priv = GET_PRIVATE(acc);
00945 qof_event_gen (&acc->inst, QOF_EVENT_DESTROY, NULL);
00946
00947 if (priv->children)
00948 {
00949 PERR (" instead of calling xaccFreeAccount(), please call \n"
00950 " xaccAccountBeginEdit(); xaccAccountDestroy(); \n");
00951
00952
00953 xaccFreeAccountChildren(acc);
00954 }
00955
00956
00957 if (priv->lots)
00958 {
00959 PERR (" instead of calling xaccFreeAccount(), please call \n"
00960 " xaccAccountBeginEdit(); xaccAccountDestroy(); \n");
00961
00962 for (lp=priv->lots; lp; lp=lp->next)
00963 {
00964 GNCLot *lot = lp->data;
00965 gnc_lot_destroy (lot);
00966 }
00967 g_list_free (priv->lots);
00968 priv->lots = NULL;
00969 }
00970
00971
00972
00973
00974
00975 if (priv->splits)
00976 {
00977 GList *slist;
00978 PERR (" instead of calling xaccFreeAccount(), please call \n"
00979 " xaccAccountBeginEdit(); xaccAccountDestroy(); \n");
00980
00981 qof_instance_reset_editlevel(acc);
00982
00983 slist = g_list_copy(priv->splits);
00984 for (lp = slist; lp; lp = lp->next) {
00985 Split *s = (Split *) lp->data;
00986 g_assert(xaccSplitGetAccount(s) == acc);
00987 xaccSplitDestroy (s);
00988 }
00989 g_list_free(slist);
00990 g_assert(priv->splits == NULL);
00991 }
00992
00993 CACHE_REPLACE(priv->accountName, NULL);
00994 CACHE_REPLACE(priv->accountCode, NULL);
00995 CACHE_REPLACE(priv->description, NULL);
00996
00997
00998
00999
01000 priv->parent = NULL;
01001 priv->children = NULL;
01002
01003 priv->balance = gnc_numeric_zero();
01004 priv->cleared_balance = gnc_numeric_zero();
01005 priv->reconciled_balance = gnc_numeric_zero();
01006
01007 priv->type = ACCT_TYPE_NONE;
01008 gnc_commodity_decrement_usage_count(priv->commodity);
01009 priv->commodity = NULL;
01010
01011 priv->balance_dirty = FALSE;
01012 priv->sort_dirty = FALSE;
01013
01014
01015 g_object_unref(acc);
01016 }
01017
01018
01019
01020
01021
01022 void
01023 xaccAccountBeginEdit (Account *acc)
01024 {
01025 g_return_if_fail(acc);
01026 qof_begin_edit(&acc->inst);
01027 }
01028
01029 static void on_done(QofInstance *inst)
01030 {
01031
01032 qof_event_gen (inst, QOF_EVENT_MODIFY, NULL);
01033 }
01034
01035 static void on_err (QofInstance *inst, QofBackendError errcode)
01036 {
01037 PERR("commit error: %d", errcode);
01038 }
01039
01040 static void acc_free (QofInstance *inst)
01041 {
01042 AccountPrivate *priv;
01043 Account *acc = (Account *) inst;
01044
01045 priv = GET_PRIVATE(acc);
01046 if (priv->parent)
01047 gnc_account_remove_child(priv->parent, acc);
01048 xaccFreeAccount(acc);
01049 }
01050
01051 static void
01052 destroy_pending_splits_for_account(QofInstance *ent, gpointer acc)
01053 {
01054 Transaction *trans = (Transaction *) ent;
01055 Split *split;
01056
01057 if (xaccTransIsOpen(trans))
01058 while ((split = xaccTransFindSplitByAccount(trans, acc)))
01059 xaccSplitDestroy(split);
01060 }
01061
01062 void
01063 xaccAccountCommitEdit (Account *acc)
01064 {
01065 AccountPrivate *priv;
01066 QofBook *book;
01067
01068 g_return_if_fail(acc);
01069 if (!qof_commit_edit(&acc->inst)) return;
01070
01071
01072
01073 priv = GET_PRIVATE(acc);
01074 if (qof_instance_get_destroying(acc))
01075 {
01076 GList *lp, *slist;
01077 QofCollection *col;
01078
01079 qof_instance_increase_editlevel(acc);
01080
01081
01082 xaccFreeAccountChildren(acc);
01083
01084 PINFO ("freeing splits for account %p (%s)",
01085 acc, priv->accountName ? priv->accountName : "(null)");
01086
01087 slist = g_list_copy(priv->splits);
01088 for (lp = slist; lp; lp = lp->next)
01089 {
01090 Split *s = lp->data;
01091 xaccSplitDestroy (s);
01092 }
01093 g_list_free(slist);
01094
01095
01096
01097
01098
01099
01100
01101
01102 book = qof_instance_get_book(acc);
01103 if (!qof_book_shutting_down(book)) {
01104 col = qof_book_get_collection(book, GNC_ID_TRANS);
01105 qof_collection_foreach(col, destroy_pending_splits_for_account, acc);
01106 }
01107
01108
01109 for (lp = priv->lots; lp; lp = lp->next)
01110 {
01111 GNCLot *lot = lp->data;
01112 gnc_lot_destroy (lot);
01113 }
01114 g_list_free(priv->lots);
01115 priv->lots = NULL;
01116
01117 qof_instance_set_dirty(&acc->inst);
01118 qof_instance_decrease_editlevel(acc);
01119 }
01120 else
01121 {
01122 xaccAccountBringUpToDate(acc);
01123 }
01124
01125 qof_commit_edit_part2(&acc->inst, on_err, on_done, acc_free);
01126 }
01127
01128 void
01129 xaccAccountDestroy (Account *acc)
01130 {
01131 g_return_if_fail(GNC_IS_ACCOUNT(acc));
01132
01133 qof_instance_set_destroying(acc, TRUE);
01134
01135 xaccAccountCommitEdit (acc);
01136 }
01137
01138
01139
01140
01141 static gboolean
01142 xaccAcctChildrenEqual(const GList *na,
01143 const GList *nb,
01144 gboolean check_guids)
01145 {
01146 if ((!na && nb) || (na && !nb))
01147 {
01148 PWARN ("only one has accounts");
01149 return(FALSE);
01150 }
01151
01152 while (na && nb)
01153 {
01154 Account *aa = na->data;
01155 Account *ab = nb->data;
01156
01157 if (!xaccAccountEqual(aa, ab, check_guids))
01158 {
01159 char sa[GUID_ENCODING_LENGTH + 1];
01160 char sb[GUID_ENCODING_LENGTH + 1];
01161
01162 guid_to_string_buff (xaccAccountGetGUID (aa), sa);
01163 guid_to_string_buff (xaccAccountGetGUID (ab), sb);
01164
01165 PWARN ("accounts %s and %s differ", sa, sb);
01166
01167 return(FALSE);
01168 }
01169
01170 na = na->next;
01171 nb = nb->next;
01172 }
01173
01174 if (na || nb)
01175 {
01176 PWARN ("different numbers of accounts");
01177 return(FALSE);
01178 }
01179
01180 return(TRUE);
01181 }
01182
01183 gboolean
01184 xaccAccountEqual(const Account *aa, const Account *ab, gboolean check_guids)
01185 {
01186 AccountPrivate *priv_aa, *priv_ab;
01187
01188 if(!aa && !ab) return TRUE;
01189
01190 g_return_val_if_fail(GNC_IS_ACCOUNT(aa), FALSE);
01191 g_return_val_if_fail(GNC_IS_ACCOUNT(ab), FALSE);
01192
01193 priv_aa = GET_PRIVATE(aa);
01194 priv_ab = GET_PRIVATE(ab);
01195 if (priv_aa->type != priv_ab->type)
01196 {
01197 PWARN ("types differ: %d vs %d", priv_aa->type, priv_ab->type);
01198 return FALSE;
01199 }
01200
01201 if (safe_strcmp(priv_aa->accountName, priv_ab->accountName) != 0)
01202 {
01203 PWARN ("names differ: %s vs %s", priv_aa->accountName, priv_ab->accountName);
01204 return FALSE;
01205 }
01206
01207 if (safe_strcmp(priv_aa->accountCode, priv_ab->accountCode) != 0)
01208 {
01209 PWARN ("codes differ: %s vs %s", priv_aa->accountCode, priv_ab->accountCode);
01210 return FALSE;
01211 }
01212
01213 if (safe_strcmp(priv_aa->description, priv_ab->description) != 0)
01214 {
01215 PWARN ("descriptions differ: %s vs %s", priv_aa->description, priv_ab->description);
01216 return FALSE;
01217 }
01218
01219 if (!gnc_commodity_equal(priv_aa->commodity, priv_ab->commodity))
01220 {
01221 PWARN ("commodities differ");
01222 return FALSE;
01223 }
01224
01225 if(check_guids) {
01226 if(qof_instance_guid_compare(aa, ab) != 0)
01227 {
01228 PWARN ("GUIDs differ");
01229 return FALSE;
01230 }
01231 }
01232
01233 if (kvp_frame_compare(aa->inst.kvp_data, ab->inst.kvp_data) != 0)
01234 {
01235 char *frame_a;
01236 char *frame_b;
01237
01238 frame_a = kvp_frame_to_string (aa->inst.kvp_data);
01239 frame_b = kvp_frame_to_string (ab->inst.kvp_data);
01240
01241