Account.c

00001 /********************************************************************\
00002  * Account.c -- Account data structure implementation               *
00003  * Copyright (C) 1997 Robin D. Clark                                *
00004  * Copyright (C) 1997-2003 Linas Vepstas <linas@linas.org>          *
00005  *                                                                  *
00006  * This program is free software; you can redistribute it and/or    *
00007  * modify it under the terms of the GNU General Public License as   *
00008  * published by the Free Software Foundation; either version 2 of   *
00009  * the License, or (at your option) any later version.              *
00010  *                                                                  *
00011  * This program is distributed in the hope that it will be useful,  *
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
00014  * GNU General Public License for more details.                     *
00015  *                                                                  *
00016  * You should have received a copy of the GNU General Public License*
00017  * along with this program; if not, contact:                        *
00018  *                                                                  *
00019  * Free Software Foundation           Voice:  +1-617-542-5942       *
00020  * 59 Temple Place - Suite 330        Fax:    +1-617-542-2652       *
00021  * Boston, MA  02111-1307,  USA       gnu@gnu.org                   *
00022  *                                                                  *
00023 \********************************************************************/
00024 
00025 #include "config.h"
00026 
00027 #include <glib.h>
00028 #include <stdlib.h>
00029 #include <string.h>
00030 
00031 #include "AccountP.h"
00032 #include "Group.h"
00033 #include "GroupP.h"
00034 #include "TransactionP.h"
00035 #include "gnc-date.h"
00036 #include "gnc-engine.h"
00037 #include "gnc-engine-util.h"
00038 #include "gnc-event.h"
00039 #include "gnc-lot.h"
00040 #include "gnc-lot-p.h"
00041 #include "gnc-pricedb.h"
00042 #include "gnc-trace.h"
00043 #include "kvp_frame.h"
00044 #include "kvp-util.h"
00045 #include "messages.h"
00046 #include "policy.h"
00047 
00048 #include "qofbackend.h"
00049 #include "qof-be-utils.h"
00050 #include "qofbook.h"
00051 #include "qofquery.h"
00052 #include "qofclass.h"
00053 #include "qofid-p.h"
00054 #include "qofinstance-p.h"
00055 #include "qofobject.h"
00056 
00057 static short module = MOD_ACCOUNT;
00058 
00059 
00060 /********************************************************************\
00061  * Because I can't use C++ for this project, doesn't mean that I    *
00062  * can't pretend to!  These functions perform actions on the        *
00063  * account data structure, in order to encapsulate the knowledge    *
00064  * of the internals of the Account in one file.                     *
00065 \********************************************************************/
00066 
00067 static void xaccAccountBringUpToDate (Account *);
00068 
00069 /********************************************************************\
00070 \********************************************************************/
00071 
00072 G_INLINE_FUNC void mark_account (Account *account);
00073 G_INLINE_FUNC void
00074 mark_account (Account *account)
00075 {
00076   if (account->parent) account->parent->saved = FALSE;
00077   account->inst.dirty = TRUE;
00078 }
00079 
00080 /********************************************************************\
00081 \********************************************************************/
00082 
00083 static void
00084 xaccInitAccount (Account * acc, QofBook *book)
00085 {
00086   ENTER ("book=%p\n", book);
00087   qof_instance_init (&acc->inst, GNC_ID_ACCOUNT, book);
00088 
00089   acc->parent   = NULL;
00090   acc->children = NULL;
00091 
00092   acc->balance = gnc_numeric_zero();
00093   acc->cleared_balance = gnc_numeric_zero();
00094   acc->reconciled_balance = gnc_numeric_zero();
00095 
00096   acc->starting_balance = gnc_numeric_zero();
00097   acc->starting_cleared_balance = gnc_numeric_zero();
00098   acc->starting_reconciled_balance = gnc_numeric_zero();
00099 
00100   acc->type = NO_TYPE;
00101 
00102   acc->accountName = g_strdup("");
00103   acc->accountCode = g_strdup("");
00104   acc->description = g_strdup("");
00105 
00106   acc->idata = 0;
00107 
00108   acc->commodity     = NULL;
00109   acc->commodity_scu = 0;
00110   acc->non_standard_scu = FALSE;
00111 
00112   acc->splits = NULL;
00113   acc->lots = NULL;
00114   acc->policy = xaccGetFIFOPolicy();
00115 
00116   acc->version = 0;
00117   acc->version_check = 0;
00118   acc->balance_dirty = FALSE;
00119   acc->sort_dirty = FALSE;
00120 
00121   LEAVE ("account=%p\n", acc);
00122 }
00123 
00124 /********************************************************************\
00125 \********************************************************************/
00126 
00127 Account *
00128 xaccMallocAccount (QofBook *book)
00129 {
00130   Account *acc;
00131 
00132   g_return_val_if_fail (book, NULL);
00133 
00134   acc = g_new (Account, 1);
00135   xaccInitAccount (acc, book);
00136   gnc_engine_gen_event (&acc->inst.entity, GNC_EVENT_CREATE);
00137 
00138   return acc;
00139 }
00140 
00141 Account *
00142 xaccCloneAccountSimple(const Account *from, QofBook *book)
00143 {
00144     Account *ret;
00145 
00146     if (!from || !book) return NULL;
00147     ENTER (" ");
00148 
00149     ret = g_new (Account, 1);
00150     g_return_val_if_fail (ret, NULL);
00151 
00152     xaccInitAccount (ret, book);
00153 
00154     /* Do not Begin/CommitEdit() here; give the caller
00155      * a chance to fix things up, and let them do it.
00156      * Also let caller issue the generate_event (EVENT_CREATE) */
00157     ret->type = from->type;
00158 
00159     ret->accountName = g_strdup(from->accountName);
00160     ret->accountCode = g_strdup(from->accountCode);
00161     ret->description = g_strdup(from->description);
00162 
00163     ret->inst.kvp_data    = kvp_frame_copy(from->inst.kvp_data);
00164 
00165     /* The new book should contain a commodity that matches
00166      * the one in the old book. Find it, use it. */
00167     ret->commodity = gnc_commodity_obtain_twin (from->commodity, book);
00168 
00169     ret->commodity_scu = from->commodity_scu;
00170     ret->non_standard_scu = from->non_standard_scu;
00171     ret->inst.dirty   = TRUE;
00172 
00173     LEAVE (" ");
00174     return ret;
00175 }
00176 
00177 Account *
00178 xaccCloneAccount (const Account *from, QofBook *book)
00179 {
00180     Account *ret;
00181 
00182     if (!from || !book) return NULL;
00183     ENTER (" ");
00184 
00185     ret = g_new (Account, 1);
00186     g_return_val_if_fail (ret, NULL);
00187 
00188     xaccInitAccount (ret, book);
00189 
00190     /* Do not Begin/CommitEdit() here; give the caller
00191      * a chance to fix things up, and let them do it.
00192      * Also let caller issue the generate_event (EVENT_CREATE) */
00193     ret->type = from->type;
00194 
00195     ret->accountName = g_strdup(from->accountName);
00196     ret->accountCode = g_strdup(from->accountCode);
00197     ret->description = g_strdup(from->description);
00198 
00199     ret->inst.kvp_data    = kvp_frame_copy(from->inst.kvp_data);
00200 
00201     /* The new book should contain a commodity that matches
00202      * the one in the old book. Find it, use it. */
00203     ret->commodity = gnc_commodity_obtain_twin (from->commodity, book);
00204 
00205     ret->commodity_scu = from->commodity_scu;
00206     ret->non_standard_scu = from->non_standard_scu;
00207 
00208     qof_instance_gemini (&ret->inst, (QofInstance *) &from->inst);
00209 
00210     LEAVE (" ");
00211     return ret;
00212 }
00213 
00214 /********************************************************************\
00215 \********************************************************************/
00216 
00217 void
00218 xaccFreeAccount (Account *acc)
00219 {
00220   Transaction *t;
00221   GList *lp;
00222 
00223   if (!acc) return;
00224 
00225   gnc_engine_gen_event (&acc->inst.entity, GNC_EVENT_DESTROY);
00226 
00227   if (acc->children)
00228   {
00229     PERR (" instead of calling xaccFreeAccount(), please call \n"
00230           " xaccAccountBeginEdit(); xaccAccountDestroy(); \n");
00231 
00232     /* First, recursively free children */
00233     xaccFreeAccountGroup (acc->children);
00234     acc->children = NULL;
00235   }
00236 
00237   /* remove lots -- although these should be gone by now. */
00238   if (acc->lots)
00239   {
00240     PERR (" instead of calling xaccFreeAccount(), please call \n"
00241           " xaccAccountBeginEdit(); xaccAccountDestroy(); \n");
00242 
00243     for (lp=acc->lots; lp; lp=lp->next)
00244     {
00245       GNCLot *lot = lp->data;
00246       gnc_lot_destroy (lot);
00247     }
00248     g_list_free (acc->lots);
00249     acc->lots = NULL;
00250   }
00251 
00252   /* Next, clean up the splits */
00253   /* NB there shouldn't be any splits by now ... they should
00254    * have been all been freed by CommitEdit().  We can remove this
00255    * check once we know the warning isn't occurring any more. */
00256   if (acc->splits)
00257   {
00258     PERR (" instead of calling xaccFreeAccount(), please call \n"
00259           " xaccAccountBeginEdit(); xaccAccountDestroy(); \n");
00260 
00261     /* any split pointing at this account needs to be unmarked */
00262     for(lp = acc->splits; lp; lp = lp->next)
00263     {
00264       Split *s = lp->data;
00265       s->acc = NULL;
00266     }
00267 
00268     acc->inst.editlevel = 0;
00269 
00270     for(lp = acc->splits; lp; lp = lp->next) {
00271       Split *s = (Split *) lp->data;
00272       t = s->parent;
00273       xaccTransBeginEdit (t);
00274       xaccSplitDestroy (s);
00275       xaccTransCommitEdit (t);
00276     }
00277 
00278     /* free up array of split pointers */
00279     g_list_free(acc->splits);
00280     acc->splits = NULL;
00281   }
00282 
00283   if (acc->accountName) g_free (acc->accountName);
00284   acc->accountName = NULL;
00285   if (acc->accountCode) g_free (acc->accountCode);
00286   acc->accountCode = NULL;
00287   if (acc->description) g_free (acc->description);
00288   acc->description = NULL;
00289 
00290   /* zero out values, just in case stray
00291    * pointers are pointing here. */
00292 
00293   acc->commodity = NULL;
00294   acc->parent   = NULL;
00295   acc->children = NULL;
00296 
00297   acc->balance  = gnc_numeric_zero();
00298   acc->cleared_balance = gnc_numeric_zero();
00299   acc->reconciled_balance = gnc_numeric_zero();
00300 
00301   acc->type = NO_TYPE;
00302   acc->accountName = NULL;
00303   acc->description = NULL;
00304   acc->commodity   = NULL;
00305 
00306   acc->version = 0;
00307   acc->balance_dirty = FALSE;
00308   acc->sort_dirty = FALSE;
00309 
00310   qof_instance_release (&acc->inst);
00311   g_free(acc);
00312 }
00313 
00314 /********************************************************************\
00315  * transactional routines
00316 \********************************************************************/
00317 
00318 void
00319 xaccAccountBeginEdit (Account *acc)
00320 {
00321   QOF_BEGIN_EDIT (&acc->inst);
00322 }
00323 
00324 static inline void noop(QofInstance *inst) {}
00325 
00326 static inline void on_err (QofInstance *inst, QofBackendError errcode)
00327 {
00328   PERR("commit error: %d", errcode);
00329 }
00330 
00331 static inline void acc_free (QofInstance *inst)
00332 {
00333   Account *acc = (Account *) inst;
00334   xaccGroupRemoveAccount(acc->parent, acc);
00335   xaccFreeAccount(acc);
00336 }
00337 
00338 void
00339 xaccAccountCommitEdit (Account *acc)
00340 {
00341   QOF_COMMIT_EDIT_PART1 (&acc->inst);
00342 
00343   /* If marked for deletion, get rid of subaccounts first,
00344    * and then the splits ... */
00345   if (acc->inst.do_free)
00346   {
00347     GList *lp;
00348 
00349     acc->inst.editlevel++;
00350 
00351     /* First, recursively free children */
00352     xaccFreeAccountGroup (acc->children);
00353     acc->children = NULL;
00354 
00355     PINFO ("freeing splits for account %p (%s)",
00356            acc, acc->accountName ? acc->accountName : "(null)");
00357 
00358     while (acc->splits)
00359     {
00360       Split *s = acc->splits->data;
00361       Transaction *t = s->parent;
00362 
00363       xaccTransBeginEdit (t);
00364       xaccSplitDestroy (s);
00365       xaccTransCommitEdit (t);
00366     }
00367 
00368     /* the lots should be empty by now */
00369     for (lp=acc->lots; lp; lp=lp->next)
00370     {
00371       GNCLot *lot = lp->data;
00372       gnc_lot_destroy (lot);
00373     }
00374     g_list_free (acc->lots);
00375     acc->lots = NULL;
00376 
00377     acc->inst.dirty = TRUE;
00378     acc->inst.editlevel--;
00379   }
00380   else
00381   {
00382     xaccAccountBringUpToDate(acc);
00383 
00384     /* force re-sort of parent group */
00385     xaccGroupInsertAccount(acc->parent, acc);
00386   }
00387 
00388   QOF_COMMIT_EDIT_PART2 (&acc->inst, on_err, noop, acc_free);
00389 
00390   gnc_engine_gen_event (&acc->inst.entity, GNC_EVENT_MODIFY);
00391 }
00392 
00393 void
00394 xaccAccountDestroy (Account *acc)
00395 {
00396   if (!acc) return;
00397   acc->inst.do_free = TRUE;
00398 
00399   xaccAccountCommitEdit (acc);
00400 }
00401 
00402 void
00403 xaccAccountSetVersion (Account *acc, gint32 vers)
00404 {
00405   if (!acc) return;
00406   acc->version = vers;
00407 }
00408 
00409 gint32
00410 xaccAccountGetVersion (Account *acc)
00411 {
00412   if (!acc) return 0;
00413   return (acc->version);
00414 }
00415 
00416 /********************************************************************\
00417 \********************************************************************/
00418 
00419 gboolean
00420 xaccAccountEqual(Account *aa, Account *ab, gboolean check_guids)
00421 {
00422   if(!aa && !ab) return TRUE;
00423 
00424   if(!aa || !ab)
00425   {
00426     PWARN ("one is NULL");
00427     return FALSE;
00428   }
00429 
00430   if (aa->type != ab->type)
00431   {
00432     PWARN ("types differ: %d vs %d", aa->type, ab->type);
00433     return FALSE;
00434   }
00435 
00436   if (safe_strcmp(aa->accountName, ab->accountName) != 0)
00437   {
00438     PWARN ("names differ: %s vs %s", aa->accountName, ab->accountName);
00439     return FALSE;
00440   }
00441 
00442   if (safe_strcmp(aa->accountCode, ab->accountCode) != 0)
00443   {
00444     PWARN ("codes differ: %s vs %s", aa->accountCode, ab->accountCode);
00445     return FALSE;
00446   }
00447 
00448   if (safe_strcmp(aa->description, ab->description) != 0)
00449   {
00450     PWARN ("descriptions differ: %s vs %s", aa->description, ab->description);
00451     return FALSE;
00452   }
00453 
00454   if (!gnc_commodity_equal(aa->commodity, ab->commodity))
00455   {
00456     PWARN ("commodities differ");
00457     return FALSE;
00458   }
00459 
00460   if(check_guids) {
00461     if(!guid_equal(&aa->inst.entity.guid, &ab->inst.entity.guid))
00462     {
00463       PWARN ("GUIDs differ");
00464       return FALSE;
00465     }
00466   }
00467 
00468   if (kvp_frame_compare(aa->inst.kvp_data, ab->inst.kvp_data) != 0)
00469   {
00470     char *frame_a;
00471     char *frame_b;
00472 
00473     frame_a = kvp_frame_to_string (aa->inst.kvp_data);
00474     frame_b = kvp_frame_to_string (ab->inst.kvp_data);
00475 
00476     PWARN ("kvp frames differ:\n%s\n\nvs\n\n%s", frame_a, frame_b);
00477 
00478     g_free (frame_a);
00479     g_free (frame_b);
00480 
00481     return FALSE;
00482   }
00483 
00484   if (!gnc_numeric_equal (aa->starting_balance, ab->starting_balance))
00485   {
00486     char *str_a;
00487     char *str_b;
00488 
00489     str_a = gnc_numeric_to_string (aa->starting_balance);
00490     str_b = gnc_numeric_to_string (ab->starting_balance);
00491 
00492     PWARN ("starting balances differ: %s vs %s", str_a, str_b);
00493 
00494     g_free (str_a);
00495     g_free (str_b);
00496 
00497     return FALSE;
00498   }
00499 
00500   if (!gnc_numeric_equal (aa->starting_cleared_balance,
00501                           ab->starting_cleared_balance))
00502   {
00503     char *str_a;
00504     char *str_b;
00505 
00506     str_a = gnc_numeric_to_string (aa->starting_cleared_balance);
00507     str_b = gnc_numeric_to_string (ab->starting_cleared_balance);
00508 
00509     PWARN ("starting cleared balances differ: %s vs %s", str_a, str_b);
00510 
00511     g_free (str_a);
00512     g_free (str_b);
00513 
00514     return FALSE;
00515   }
00516 
00517   if (!gnc_numeric_equal (aa->starting_reconciled_balance,
00518                           ab->starting_reconciled_balance))
00519   {
00520     char *str_a;
00521     char *str_b;
00522 
00523     str_a = gnc_numeric_to_string (aa->starting_reconciled_balance);
00524     str_b = gnc_numeric_to_string (ab->starting_reconciled_balance);
00525 
00526     PWARN ("starting reconciled balances differ: %s vs %s", str_a, str_b);
00527 
00528     g_free (str_a);
00529     g_free (str_b);
00530 
00531     return FALSE;
00532   }
00533 
00534   if (!gnc_numeric_equal (aa->balance, ab->balance))
00535   {
00536     char *str_a;
00537     char *str_b;
00538 
00539     str_a = gnc_numeric_to_string (aa->balance);
00540     str_b = gnc_numeric_to_string (ab->balance);
00541 
00542     PWARN ("balances differ: %s vs %s", str_a, str_b);
00543 
00544     g_free (str_a);
00545     g_free (str_b);
00546 
00547     return FALSE;
00548   }
00549 
00550   if (!gnc_numeric_equal (aa->cleared_balance, ab->cleared_balance))
00551   {
00552     char *str_a;
00553     char *str_b;
00554 
00555     str_a = gnc_numeric_to_string (aa->cleared_balance);
00556     str_b = gnc_numeric_to_string (ab->cleared_balance);
00557 
00558     PWARN ("cleared balances differ: %s vs %s", str_a, str_b);
00559 
00560     g_free (str_a);
00561     g_free (str_b);
00562 
00563     return FALSE;
00564   }
00565 
00566   if (!gnc_numeric_equal (aa->reconciled_balance, ab->reconciled_balance))
00567   {
00568     char *str_a;
00569     char *str_b;
00570 
00571     str_a = gnc_numeric_to_string (aa->reconciled_balance);
00572     str_b = gnc_numeric_to_string (ab->reconciled_balance);
00573 
00574     PWARN ("reconciled balances differ: %s vs %s", str_a, str_b);
00575 
00576     g_free (str_a);
00577     g_free (str_b);
00578 
00579     return FALSE;
00580   }
00581 
00582   /* no parent; always compare downwards. */
00583 
00584   {
00585     GList *la = aa->splits;
00586     GList *lb = ab->splits;
00587 
00588     if ((la && !lb) || (!la && lb))
00589     {
00590       PWARN ("only one has splits");
00591       return FALSE;
00592     }
00593 
00594     if(la && lb)
00595     {
00596       /* presume that the splits are in the same order */
00597       while (la && lb)
00598       {
00599         Split *sa = (Split *) la->data;
00600         Split *sb = (Split *) lb->data;
00601 
00602         if (!xaccSplitEqual(sa, sb, check_guids, TRUE, FALSE))
00603         {
00604           PWARN ("splits differ");
00605           return(FALSE);
00606         }
00607 
00608         la = la->next;
00609         lb = lb->next;
00610       }
00611 
00612       if ((la != NULL) || (lb != NULL))
00613       {
00614         PWARN ("number of splits differs");
00615         return(FALSE);
00616       }
00617     }
00618   }
00619 
00620   if (!xaccGroupEqual(aa->children, ab->children, check_guids))
00621   {
00622     PWARN ("children differ");
00623     return FALSE;
00624   }
00625 
00626   return(TRUE);
00627 }
00628 
00629 /********************************************************************\
00630 \********************************************************************/
00631 
00632 static gint
00633 split_sort_func(gconstpointer a, gconstpointer b) {
00634   /* don't coerce xaccSplitDateOrder so we'll catch changes */
00635   Split *sa = (Split *) a;
00636   Split *sb = (Split *) b;
00637   return(xaccSplitDateOrder(sa, sb));
00638 }
00639 
00640 void
00641 xaccAccountSortSplits (Account *acc, gboolean force)
00642 {
00643   if(!acc) return;
00644   if(!acc->sort_dirty) return;
00645   if(!force && acc->inst.editlevel > 0) return;
00646 
00647   acc->splits = g_list_sort(acc->splits, split_sort_func);
00648 
00649   acc->sort_dirty = FALSE;
00650   acc->balance_dirty = TRUE;
00651 }
00652 
00653 static void
00654 xaccAccountBringUpToDate(Account *acc)
00655 {
00656   if(!acc) return;
00657 
00658   /* if a re-sort happens here, then everything will update, so the
00659      cost basis and balance calls are no-ops */
00660   xaccAccountSortSplits(acc, FALSE);
00661   xaccAccountRecomputeBalance(acc);
00662 }
00663 
00664 /********************************************************************\
00665 \********************************************************************/
00666 
00667 void
00668 xaccAccountSetGUID (Account *account, const GUID *guid)
00669 {
00670   if (!account || !guid) return;
00671 
00672   /* XXX this looks fishy and weird to me ... */
00673   PINFO("acct=%p", account);
00674   xaccAccountBeginEdit (account);
00675   qof_entity_set_guid (&account->inst.entity, guid);
00676   account->inst.dirty = TRUE;
00677   xaccAccountCommitEdit (account);
00678 }
00679 
00680 /********************************************************************\
00681 \********************************************************************/
00682 
00683 Account *
00684 xaccAccountLookup (const GUID *guid, QofBook *book)
00685 {
00686   QofCollection *col;
00687   if (!guid || !book) return NULL;
00688   col = qof_book_get_collection (book, GNC_ID_ACCOUNT);
00689   return (Account *) qof_collection_lookup_entity (col, guid);
00690 }
00691 
00692 /********************************************************************\
00693 \********************************************************************/
00694 
00695 short
00696 xaccAccountGetMark (Account *acc)
00697 {
00698   if (!acc) return 0;
00699   return acc->mark;
00700 }
00701 
00702 void
00703 xaccAccountSetMark (Account *acc, short m)
00704 {
00705   if (!acc) return;
00706   acc->mark = m;
00707 }
00708 
00709 void
00710 xaccClearMark (Account *acc, short val)
00711 {
00712   AccountGroup *topgrp;
00713 
00714   if (!acc) return;
00715   topgrp = xaccAccountGetRoot (acc);
00716 
00717   if (topgrp)
00718   {
00719     GList *list;
00720     GList *node;
00721 
00722     list = xaccGroupGetAccountList (topgrp);
00723 
00724     for (node = list; node; node = node->next)
00725     {
00726       Account *account = node->data;
00727 
00728       xaccClearMarkDown (account, val);
00729     }
00730   }
00731   else
00732     xaccClearMarkDown (acc, val);
00733 }
00734 
00735 void
00736 xaccClearMarkDown (Account *acc, short val)
00737 {
00738   AccountGroup *children;
00739 
00740   if (!acc) return;
00741   acc->mark = val;
00742 
00743   children = acc->children;
00744   if (children)
00745   {
00746     GList *list;
00747     GList *node;
00748 
00749     list = xaccGroupGetAccountList (children);
00750 
00751     for (node = list; node; node = node->next)
00752     {
00753       Account *account = node->data;
00754 
00755       xaccClearMarkDown (account, val);
00756     }
00757   }
00758 }
00759 
00760 void
00761 xaccClearMarkDownGr (AccountGroup *grp, short val)
00762 {
00763   GList *list;
00764   GList *node;
00765 
00766   if (!grp) return;
00767 
00768   list = xaccGroupGetAccountList (grp);
00769 
00770   for (node = list; node; node = node->next)
00771   {
00772     Account *account = node->data;
00773 
00774     xaccClearMarkDown (account, val);
00775   }
00776 }
00777 
00778 /********************************************************************\
00779 \********************************************************************/
00780 
00781 void
00782 xaccAccountRemoveLot (Account *acc, GNCLot *lot)
00783 {
00784   if (!acc || !lot) return;
00785    ENTER ("(acc=%p, lot=%p)", acc, lot);
00786 
00787    xaccAccountBeginEdit (acc);
00788    acc->lots = g_list_remove (acc->lots, lot);
00789    xaccAccountCommitEdit (acc);
00790    LEAVE ("(acc=%p, lot=%p)", acc, lot);
00791 }
00792 
00793 void
00794 xaccAccountInsertLot (Account *acc, GNCLot *lot)
00795 {
00796    GList *sl;
00797    Account * old_acc = NULL;
00798 
00799    if (!acc || !lot) return;
00800    ENTER ("(acc=%p, lot=%p)", acc, lot);
00801 
00802    /* pull it out of the old account */
00803    if (lot->account && lot->account != acc)
00804    {
00805       old_acc = lot->account;
00806       xaccAccountBeginEdit (old_acc);
00807       old_acc->lots = g_list_remove (old_acc->lots, lot);
00808 
00809    }
00810 
00811    xaccAccountBeginEdit(acc);
00812 
00813    /* Avoid inserting into list more than once */
00814    if (lot->account != acc)
00815    {
00816       acc->lots = g_list_prepend (acc->lots, lot);
00817       lot->account = acc;
00818    }
00819 
00820    /* Move all splits over to the new account.  At worst,
00821     * this is a no-op. */
00822    if (lot->splits)
00823    {
00824       for (sl = lot->splits; sl; sl=sl->next)
00825       {
00826          Split *s = sl->data;
00827          if (s->acc != acc)
00828          {
00829             xaccAccountInsertSplit (acc, s);
00830          }
00831       }
00832    }
00833    xaccAccountCommitEdit(acc);
00834    xaccAccountCommitEdit(old_acc);
00835    LEAVE ("(acc=%p, lot=%p)", acc, lot);
00836 }
00837 
00838 /********************************************************************\
00839 \********************************************************************/
00840 
00841 void
00842 xaccAccountInsertSplit (Account *acc, Split *split)
00843 {
00844   Transaction *trans;
00845   gnc_numeric old_amt;
00846 
00847   if (!acc) return;
00848   if (!split) return;
00849   ENTER ("(acc=%p, split=%p)", acc, split);
00850 
00851   /* check for book mix-up */
00852   g_return_if_fail (acc->inst.book == split->book);
00853 
00854   trans = xaccSplitGetParent (split);
00855   old_amt = xaccSplitGetAmount (split);
00856 
00857   xaccAccountBeginEdit(acc);
00858   xaccTransBeginEdit(trans);
00859 
00860   acc->balance_dirty = TRUE;
00861   acc->sort_dirty = TRUE;
00862 
00863   /* If this split belongs to another account, remove it from there
00864    * first.  We don't want to ever leave the system in an inconsistent
00865    * state.  Note that it might belong to the current account if we're
00866    * just using this call to re-order.  */
00867   if (split->acc && split->acc != acc)
00868   {
00869     xaccAccountRemoveSplit (split->acc, split);
00870   }
00871 
00872   split->acc = acc;
00873   if (split->lot && (NULL == split->lot->account))
00874   {
00875       xaccAccountInsertLot (acc, split->lot);
00876   }
00877 
00878   if (g_list_index(acc->splits, split) == -1)
00879   {
00880       if (acc->inst.editlevel == 1)
00881       {
00882           acc->splits = g_list_insert_sorted(acc->splits, split,
00883                                              split_sort_func);
00884           acc->sort_dirty = FALSE;
00885       }
00886       else
00887       {
00888           acc->splits = g_list_prepend(acc->splits, split);
00889       }
00890 
00891       mark_account (acc);
00892   }
00893 
00894   /* Setting the amount casues a conversion to the new account's
00895    * denominator AKA 'SCU Smallest Currency Unit'. */
00896   /* xaccSplitSetAmount(split, old_amt); */
00897   split->amount = gnc_numeric_convert (old_amt,
00898                 xaccAccountGetCommoditySCU(acc), GNC_HOW_RND_ROUND);
00899   xaccTransCommitEdit(trans);
00900   xaccAccountCommitEdit(acc);
00901   LEAVE ("(acc=%p, split=%p)", acc, split);
00902 }
00903 
00904 /********************************************************************\
00905 \********************************************************************/
00906 
00907 void
00908 xaccAccountRemoveSplit (Account *acc, Split *split)
00909 {
00910   if (!acc) return;
00911   if (!split) return;
00912   if (split->acc && split->acc != acc) return;
00913 
00914   ENTER ("(acc=%p, split=%p)", acc, split);
00915 
00916   xaccAccountBeginEdit(acc);
00917   {
00918     GList *node;
00919 
00920     node = g_list_find (acc->splits, split);
00921     if (!node)
00922     {
00923       PERR ("split not in account");
00924     }
00925     else
00926     {
00927       Transaction *trans = xaccSplitGetParent (split);
00928 
00929       acc->splits = g_list_remove_link (acc->splits, node);
00930       g_list_free_1 (node);
00931 
00932       acc->balance_dirty = TRUE;
00933 
00934       xaccTransBeginEdit (trans);
00935       split->acc = NULL;
00936 
00937       /* Remove from lot (but only if it hasn't been moved to new lot already) */
00938       if (split->lot && split->lot->account == acc)
00939       {
00940         gnc_lot_remove_split (split->lot, split);
00941       }
00942       xaccTransCommitEdit (trans);
00943 
00944       mark_account (acc);
00945       if (split->parent)
00946         gnc_engine_gen_event (&split->parent->inst.entity, GNC_EVENT_MODIFY);
00947     }
00948   }
00949   xaccAccountCommitEdit(acc);
00950   LEAVE ("(acc=%p, split=%p)", acc, split);
00951 }
00952 
00953 
00954 /********************************************************************\
00955  * xaccAccountRecomputeBalance                                      *
00956  *   recomputes the partial balances and the current balance for    *
00957  *   this account.                                                  *
00958  *                                                                  *
00959  * The way the computation is done depends on whether the partial   *
00960  * balances are for a monetary account (bank, cash, etc.) or a      *
00961  * certificate account (stock portfolio, mutual fund).  For bank    *
00962  * accounts, the invariant amount is the dollar amount. For share   *
00963  * accounts, the invariant amount is the number of shares. For      *
00964  * share accounts, the share price fluctuates, and the current      *
00965  * value of such an account is the number of shares times the       *
00966  * current share price.                                             *
00967  *                                                                  *
00968  * Part of the complexity of this computation stems from the fact   *
00969  * xacc uses a double-entry system, meaning that one transaction    *
00970  * appears in two accounts: one account is debited, and the other   *
00971  * is credited.  When the transaction represents a sale of shares,  *
00972  * or a purchase of shares, some care must be taken to compute      *
00973  * balances correctly.  For a sale of shares, the stock account must*
00974  * be debited in shares, but the bank account must be credited      *
00975  * in dollars.  Thus, two different mechanisms must be used to      *
00976  * compute balances, depending on account type.                     *
00977  *                                                                  *
00978  * Args:   account -- the account for which to recompute balances   *
00979  * Return: void                                                     *
00980 \********************************************************************/
00981 
00982 void
00983 xaccAccountRecomputeBalance (Account * acc)
00984 {
00985   gnc_numeric  balance;
00986   gnc_numeric  cleared_balance;
00987   gnc_numeric  reconciled_balance;
00988   Split *last_split = NULL;
00989   GList *lp;
00990 
00991   if (NULL == acc) return;
00992   if (acc->inst.editlevel > 0) return;
00993   if (!acc->balance_dirty) return;
00994   if (acc->inst.do_free) return;
00995 
00996   balance            = acc->starting_balance;
00997   cleared_balance    = acc->starting_cleared_balance;
00998   reconciled_balance = acc->starting_reconciled_balance;
00999 
01000   PINFO ("acct=%s starting baln=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT,
01001         acc->accountName, balance.num, balance.denom);
01002   for(lp = acc->splits; lp; lp = lp->next)
01003   {
01004     Split *split = (Split *) lp->data;
01005     gnc_numeric amt = xaccSplitGetAmount (split);
01006 
01007     balance = gnc_numeric_add_fixed(balance, amt);
01008 
01009     if (NREC != split->reconciled)
01010     {
01011       cleared_balance = gnc_numeric_add_fixed(cleared_balance, amt);
01012     }
01013 
01014     if (YREC == split->reconciled ||
01015         FREC == split->reconciled)
01016     {
01017       reconciled_balance =
01018         gnc_numeric_add_fixed(reconciled_balance, amt);
01019     }
01020 
01021     split->balance = balance;
01022     split->cleared_balance = cleared_balance;
01023     split->reconciled_balance = reconciled_balance;
01024 
01025     last_split = split;
01026   }
01027 
01028   acc->balance = balance;
01029   acc->cleared_balance = cleared_balance;
01030   acc->reconciled_balance = reconciled_balance;
01031 
01032   acc->balance_dirty = FALSE;
01033   gnc_engine_gen_event (&acc->inst.entity, GNC_EVENT_MODIFY);
01034 }
01035 
01036 /********************************************************************\
01037 \********************************************************************/
01038 
01039 void
01040 xaccAccountSetStartingBalance(Account *acc,
01041                               const gnc_numeric start_baln,
01042                               const gnc_numeric start_cleared_baln,
01043                               const gnc_numeric start_reconciled_baln)
01044 {
01045   if (!acc) return;
01046 
01047   acc->starting_balance = start_baln;
01048   acc->starting_cleared_balance = start_cleared_baln;
01049   acc->starting_reconciled_balance = start_reconciled_baln;
01050 
01051   acc->balance_dirty = TRUE;
01052 }
01053 
01054 /********************************************************************\
01055  * xaccAccountFixSplitDateOrder                                     *
01056  *   check this split to see if the date is in correct order        *
01057  *   If it is not, reorder the transactions ...                     *
01058  *                                                                  *
01059  * Args:   acc   -- the account to check                            *
01060  *         split -- the split to check                              *
01061  *                                                                  *
01062  * Return: int -- non-zero if out of order                          *
01063 \********************************************************************/
01064 
01065 void
01066 xaccAccountFixSplitDateOrder (Account * acc, Split *split)
01067 {
01068   if (NULL == acc) return;
01069   if (NULL == split) return;
01070 
01071   if (acc->inst.do_free) return;
01072 
01073   acc->sort_dirty = TRUE;
01074   acc->balance_dirty = TRUE;
01075 
01076   if (acc->inst.editlevel > 0) return;
01077 
01078   xaccAccountBringUpToDate (acc);
01079 }
01080 
01081 /********************************************************************\
01082  * xaccCheckTransDateOrder                                          *
01083  *   check this transaction to see if the date is in correct order  *
01084  *   If it is not, reorder the transactions.                        *
01085  *   This routine perfroms the check for both of the double-entry   *
01086  *   transaction entries.                                           *
01087  *                                                                  *
01088  * Args:   trans -- the transaction to check                        *
01089  * Return: int -- non-zero if out of order                          *
01090 \********************************************************************/
01091 
01092 void
01093 xaccTransFixSplitDateOrder (Transaction *trans)
01094 {
01095   GList *node;
01096 
01097   if (trans == NULL) return;
01098 
01099   gnc_engine_suspend_events();
01100   for (node = trans->splits; node; node = node->next)
01101   {
01102     Split *s = node->data;
01103     xaccAccountFixSplitDateOrder (s->acc, s);
01104   }
01105   gnc_engine_resume_events();
01106 }
01107 
01108 /********************************************************************\
01109 \********************************************************************/
01110 
01111 /* The sort order is used to implicitly define an
01112  * order for report generation */
01113 
01114 static int typeorder[NUM_ACCOUNT_TYPES] = {
01115      BANK, STOCK, MUTUAL, CURRENCY, CASH, ASSET, RECEIVABLE,
01116      CREDIT, LIABILITY, PAYABLE, INCOME, EXPENSE, EQUITY };
01117 
01118 static int revorder[NUM_ACCOUNT_TYPES] = {
01119      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
01120 
01121 
01122 int
01123 xaccAccountOrder (Account **aa, Account **ab)
01124 {
01125   char *da, *db;
01126   char *endptr = NULL;
01127   int ta, tb;
01128   long la, lb;
01129 
01130   if ( (*aa) && !(*ab) ) return -1;
01131   if ( !(*aa) && (*ab) ) return +1;
01132   if ( !(*aa) && !(*ab) ) return 0;
01133 
01134   /* sort on accountCode strings */
01135   da = (*aa)->accountCode;
01136   db = (*ab)->accountCode;
01137 
01138   /* If accountCodes are both base 36 integers do an integer sort */
01139   la = strtoul (da, &endptr, 36);
01140   if((*da != '\0') && (*endptr == '\0')) {
01141     lb = strtoul (db, &endptr, 36);
01142     if((*db != '\0') && (*endptr == '\0')) {
01143       if (la < lb) return -1;
01144       if (la > lb) return +1;
01145     }
01146   }
01147 
01148   /* Otherwise do a string sort */
01149   SAFE_STRCMP (da, db);
01150 
01151   /* if acccount-type-order array not initialized, initialize it */
01152   /* this will happen at most once during program invocation */
01153   if (-1 == revorder[0]) {
01154     int i;
01155     for (i=0; i<NUM_ACCOUNT_TYPES; i++) {
01156       revorder [typeorder[i]] = i;
01157     }
01158   }
01159 
01160   /* otherwise, sort on account type */
01161   ta = (*aa)->type;
01162   tb = (*ab)->type;
01163   ta = revorder[ta];
01164   tb = revorder[tb];
01165   if (ta < tb) return -1;
01166   if (ta > tb) return +1;
01167 
01168   /* otherwise, sort on accountName strings */
01169   da = (*aa)->accountName;
01170   db = (*ab)->accountName;
01171   SAFE_STRCMP (da, db);
01172 
01173   /* guarantee a stable sort */
01174   return guid_compare (&((*aa)->inst.entity.guid), &((*ab)->inst.entity.guid));
01175 }
01176 
01177 /********************************************************************\
01178 \********************************************************************/
01179 
01180 void
01181 xaccAccountSetType (Account *acc, GNCAccountType tip)
01182 {
01183 
01184   if (!acc) return;
01185 
01186   xaccAccountBeginEdit(acc);
01187   {
01188     /* refuse invalid account types, and don't bother if not new type. */
01189     if((NUM_ACCOUNT_TYPES > tip) && (acc->type != tip)) {
01190       acc->type = tip;
01191       acc->balance_dirty = TRUE; /* new type may affect balance computation */
01192     }
01193 
01194     mark_account (acc);
01195   }
01196   acc->inst.dirty = TRUE;
01197   xaccAccountCommitEdit(acc);
01198 }
01199 
01200 void
01201 xaccAccountSetName (Account *acc, const char *str)
01202 {
01203    char * tmp;
01204 
01205    if ((!acc) || (!str)) return;
01206 
01207    xaccAccountBeginEdit(acc);
01208    {
01209      /* make strdup before freeing (just in case str==accountName !!) */
01210      tmp = g_strdup (str);
01211      g_free (acc->accountName);
01212      acc->accountName = tmp;
01213 
01214      mark_account (acc);
01215    }
01216    acc->inst.dirty = TRUE;
01217    xaccAccountCommitEdit(acc);
01218 }
01219 
01220 void
01221 xaccAccountSetCode (Account *acc, const char *str)
01222 {
01223    char * tmp;
01224    if ((!acc) || (!str)) return;
01225 
01226    xaccAccountBeginEdit(acc);
01227    {
01228      /* make strdup before freeing */
01229      tmp = g_strdup (str);
01230      g_free (acc->accountCode);
01231      acc->accountCode = tmp;
01232 
01233      mark_account (acc);
01234    }
01235    acc->inst.dirty = TRUE;
01236    xaccAccountCommitEdit(acc);
01237 }
01238 
01239 void
01240 xaccAccountSetDescription (Account *acc, const char *str)
01241 {
01242    char * tmp;
01243    if ((!acc) || (!str)) return;
01244 
01245    xaccAccountBeginEdit(acc);
01246    {
01247      /* make strdup before freeing (just in case str==description !!) */
01248      tmp = g_strdup (str);
01249      g_free (acc->description);
01250      acc->description = tmp;
01251 
01252      mark_account (acc);
01253    }
01254    acc->inst.dirty = TRUE;
01255    xaccAccountCommitEdit(acc);
01256 }
01257 
01258 static void
01259 qofAccountSetParent (Account *acc, QofEntity *parent)
01260 {
01261         Account *parent_acc;
01262 
01263         if((!acc)||(!parent)) { return; }
01264         parent_acc = (Account*)parent;
01265         xaccAccountBeginEdit(acc);
01266         xaccAccountBeginEdit(parent_acc);
01267         xaccAccountInsertSubAccount(parent_acc, acc);
01268         mark_account (parent_acc);
01269         mark_account (acc);
01270         xaccAccountCommitEdit(acc);
01271         xaccAccountCommitEdit(parent_acc);
01272 }
01273 
01274 void
01275 xaccAccountSetNotes (Account *acc, const char *str)
01276 {
01277   if ((!acc) || (!str)) return;
01278 
01279   xaccAccountBeginEdit(acc);
01280   kvp_frame_set_slot_nc(acc->inst.kvp_data, "notes",
01281                         kvp_value_new_string(str));
01282   mark_account (acc);
01283   acc->inst.dirty = TRUE;
01284   xaccAccountCommitEdit(acc);
01285 }
01286 
01287 /* FIXME : is this the right way to do this? Uhh, I think so ?? */
01288 static void
01289 update_split_commodity(Account * acc)
01290 {
01291   GList *lp;
01292 
01293   if(!acc) return;
01294 
01295   xaccAccountBeginEdit(acc);
01296 
01297   /* iterate over splits */
01298   for (lp = acc->splits; lp; lp = lp->next)
01299   {
01300     Split *s = (Split *) lp->data;
01301     Transaction *trans = xaccSplitGetParent (s);
01302     gnc_numeric amt;
01303 
01304     amt = xaccSplitGetAmount (s);
01305     xaccTransBeginEdit (trans);
01306     xaccSplitSetAmount (s, amt);
01307     xaccTransCommitEdit (trans);
01308   }
01309 
01310   xaccAccountCommitEdit(acc);
01311 }
01312 
01313 /********************************************************************\
01314 \********************************************************************/
01315 
01316 void
01317 xaccAccountSetCommodity (Account * acc, gnc_commodity * com)
01318 {
01319   if ((!acc) || (!com)) return;
01320 
01321   xaccAccountBeginEdit(acc);
01322   {
01323     acc->commodity    = com;
01324     acc->commodity_scu = gnc_commodity_get_fraction(com);
01325     acc->non_standard_scu = FALSE;
01326     update_split_commodity(acc);
01327 
01328     acc->sort_dirty = TRUE;
01329     acc->balance_dirty = TRUE;
01330 
01331     mark_account (acc);
01332   }
01333   acc->inst.dirty = TRUE;
01334 
01335   if (gnc_commodity_is_iso(com))
01336   {
01337     /* compatability hack - Gnucash 1.8 gets currency quotes when a
01338        non-default currency is assigned to an account.  */
01339     gnc_commodity_set_quote_flag(com, TRUE);
01340   }
01341   xaccAccountCommitEdit(acc);
01342 }
01343 
01344 /*
01345  * Set the account scu and then check to see if it is the same as the
01346  * commodity scu.  This function is called when parsing the data file
01347  * and is designed to catch cases where the two were accidentally set
01348  * to mismatched values in the past.
01349  */
01350 void
01351 xaccAccountSetCommoditySCU (Account *acc, int scu)
01352 {
01353   if (!acc) return;
01354 
01355   xaccAccountBeginEdit(acc);
01356   {
01357     acc->commodity_scu = scu;
01358     if (scu != gnc_commodity_get_fraction(acc->commodity))
01359       acc->non_standard_scu = TRUE;
01360     mark_account (acc);
01361   }
01362   acc->inst.dirty = TRUE;
01363   xaccAccountCommitEdit(acc);
01364 }
01365 
01366 int
01367 xaccAccountGetCommoditySCUi (Account * acc)
01368 {
01369   if (!acc) return 0;
01370 
01371   return acc->commodity_scu;
01372 }
01373 
01374 int
01375 xaccAccountGetCommoditySCU (Account * acc)
01376 {
01377   if (!acc) return 0;
01378 
01379   if (acc->non_standard_scu || !acc->commodity)
01380     return acc->commodity_scu;
01381   return gnc_commodity_get_fraction(acc->commodity);
01382 }
01383 
01384 void
01385 xaccAccountSetNonStdSCU (Account *acc, gboolean flag)
01386 {
01387   if (!acc) return;
01388 
01389   xaccAccountBeginEdit(acc);
01390   {
01391     acc->non_standard_scu = flag;
01392     mark_account (acc);
01393   }
01394   acc->inst.dirty = TRUE;
01395   xaccAccountCommitEdit(acc);
01396 }
01397 
01398 gboolean
01399 xaccAccountGetNonStdSCU (Account * acc)
01400 {
01401   if (!acc) return 0;
01402 
01403   return acc->non_standard_scu;
01404 }
01405 
01406 /********************************************************************\
01407 \********************************************************************/
01408 /* below follow the old, deprecated currency/security routines. */
01409 
01410 void
01411 DxaccAccountSetCurrency (Account * acc, gnc_commodity * currency)
01412 {
01413   const char *string;
01414   gnc_commodity *commodity;
01415 
01416   if ((!acc) || (!currency)) return;
01417 
01418   xaccAccountBeginEdit(acc);
01419   string = gnc_commodity_get_unique_name (currency);
01420   kvp_frame_set_slot_nc(acc->inst.kvp_data, "old-currency",
01421                         kvp_value_new_string(string));
01422   mark_account (acc);
01423   acc->inst.dirty = TRUE;
01424   xaccAccountCommitEdit(acc);
01425 
01426   commodity = DxaccAccountGetCurrency (acc);
01427   if (!commodity)
01428   {
01429     gnc_commodity_table_insert (gnc_commodity_table_get_table (acc->inst.book), currency);
01430   }
01431 }
01432 
01433 /********************************************************************\
01434 \********************************************************************/
01435 
01436 AccountGroup *
01437 xaccAccountGetChildren (Account *acc)
01438 {
01439    if (!acc) return NULL;
01440    return (acc->children);
01441 }
01442 
01443 AccountGroup *
01444 xaccAccountGetParent (Account *acc)
01445 {
01446    if (!acc) return NULL;
01447    return (acc->parent);
01448 }
01449 
01450 Account *
01451 xaccAccountGetParentAccount (Account * acc)
01452 {
01453   if (!acc) return NULL;
01454   return xaccGroupGetParentAccount(acc->parent);
01455 }
01456 
01457 GList *
01458 xaccAccountGetDescendants (Account *acc)
01459 {
01460    GList *accounts;
01461 
01462    if (!acc) return NULL;
01463    accounts = xaccGroupGetSubAccounts(acc->children);
01464    return (accounts);
01465 }
01466 
01467 GNCAccountType
01468 xaccAccountGetType (Account *acc)
01469 {
01470    if (!acc) return NO_TYPE;
01471    return (acc->type);
01472 }
01473 
01474 static const char*
01475 qofAccountGetTypeString (Account *acc)
01476 {
01477         if(!acc) { return NULL; }
01478         return (xaccAccountTypeEnumAsString(acc->type));
01479 }
01480 
01481 static void
01482 qofAccountSetType (Account *acc, const char *type_string)
01483 {
01484         GNCAccountType type;
01485 
01486         type = xaccAccountStringToEnum(type_string);
01487         xaccAccountSetType(acc, type);
01488 }
01489 
01490 const char *
01491 xaccAccountGetName (Account *acc)
01492 {
01493    if (!acc) return NULL;
01494    return (acc->accountName);
01495 }
01496 
01497 char *
01498 xaccAccountGetFullName(Account *account, const char separator)
01499 {
01500   Account *a;
01501   char *fullname;
01502   const char *name;
01503   char *p;
01504   int length;
01505 
01506   if (account == NULL)
01507     return g_strdup("");
01508 
01509   /* Figure out how much space is needed */
01510   length = 0;
01511   a = account;
01512   while (a != NULL)
01513   {
01514     name = a->accountName;
01515 
01516     length += strlen(name) + 1; /* plus one for the separator */
01517 
01518     a = xaccAccountGetParentAccount(a);
01519   }
01520 
01521   /* length has one extra separator in it, that's ok, because it will
01522    * hold the null character at the end. */
01523 
01524   /* allocate the memory */
01525   fullname = g_new(char, length);
01526 
01527   /* go to end of string */
01528   p = fullname + length - 1;
01529 
01530   /* put in the null character and move to the previous char */
01531   *p-- = 0;
01532 
01533   a = account;
01534   while (a != NULL)
01535   {
01536     name = a->accountName;
01537     length = strlen(name);
01538 
01539     /* copy the characters going backwards */
01540     while (length > 0)
01541       *p-- = name[--length];
01542 
01543     a = xaccAccountGetParentAccount(a);
01544 
01545     /* if we're not at the root, add another separator */
01546     if (a != NULL)
01547       *p-- = separator;
01548   }
01549 
01550   return fullname;
01551 }
01552 
01553 const char *
01554 xaccAccountGetCode (Account *acc)
01555 {
01556    if (!acc) return NULL;
01557    return (acc->accountCode);
01558 }
01559 
01560 const char *
01561 xaccAccountGetDescription (Account *acc)
01562 {
01563    if (!acc) return NULL;
01564    return (acc->description);
01565 }
01566 
01567 const char *
01568 xaccAccountGetNotes (Account *acc)
01569 {
01570   KvpValue *v;
01571 
01572   if (!acc) return NULL;
01573   v = kvp_frame_get_slot(acc->inst.kvp_data, "notes");
01574   if(v) return(kvp_value_get_string(v));
01575   return(NULL);
01576 }
01577 
01578 gnc_commodity *
01579 DxaccAccountGetCurrency (Account *acc)
01580 {
01581   KvpValue *v;
01582   const char *s;
01583   gnc_commodity_table *table;
01584 
01585   if (!acc) return NULL;
01586 
01587   v = kvp_frame_get_slot(acc->inst.kvp_data, "old-currency");
01588   if (!v) return NULL;
01589 
01590   s = kvp_value_get_string (v);
01591   if (!s) return NULL;
01592 
01593   table = gnc_commodity_table_get_table (acc->inst.book);
01594 
01595   return gnc_commodity_table_lookup_unique (table, s);
01596 }
01597 
01598 gnc_commodity *
01599 xaccAccountGetCommodity (Account *acc)
01600 {
01601   if (!acc) return NULL;
01602 
01603   return (acc->commodity);
01604 }
01605 
01606 gnc_numeric
01607 xaccAccountGetBalance (Account *acc)
01608 {
01609   if (!acc) return gnc_numeric_zero();
01610   return acc->balance;
01611 }
01612 
01613 gnc_numeric
01614 xaccAccountGetClearedBalance (Account *acc)
01615 {
01616    if (!acc) return gnc_numeric_zero();
01617    return acc->cleared_balance;
01618 }
01619 
01620 gnc_numeric
01621 xaccAccountGetReconciledBalance (Account *acc)
01622 {
01623    if (!acc) return gnc_numeric_zero();
01624    return acc->reconciled_balance;
01625 }
01626 
01627 gnc_numeric
01628 xaccAccountGetProjectedMinimumBalance (Account *account)
01629 {
01630   GList *node;
01631   time_t today;
01632   gnc_numeric lowest = gnc_numeric_zero ();
01633   int seen_a_transaction = 0;
01634 
01635   if (!account)
01636     return gnc_numeric_zero ();
01637 
01638   today = gnc_timet_get_today_end();
01639   for (node = g_list_last (account->splits); node; node = node->prev)
01640   {
01641     Split *split = node->data;
01642 
01643     if (!seen_a_transaction)
01644     {
01645       lowest = xaccSplitGetBalance (split);
01646       seen_a_transaction = 1;
01647     } else if (gnc_numeric_compare(xaccSplitGetBalance (split), lowest) < 0) {
01648       lowest = xaccSplitGetBalance (split);
01649     }
01650 
01651     if (xaccTransGetDate (xaccSplitGetParent (split)) <= today)
01652       return lowest;
01653   }
01654 
01655   return lowest;
01656 }
01657 
01658 
01659 /********************************************************************\
01660 \********************************************************************/
01661 
01662 gnc_numeric
01663 xaccAccountGetBalanceAsOfDate (Account *acc, time_t date)
01664 {
01665   /* Ideally this could use xaccAccountForEachSplit, but
01666    * it doesn't exist yet and I'm uncertain of exactly how
01667    * it would work at this time, since it differs from
01668    * xaccAccountForEachTransaction by using gpointer return
01669    * values rather than gints.
01670    */
01671   GList   *lp;
01672   Timespec ts, trans_ts;
01673   gboolean found = FALSE;
01674   gnc_numeric balance;
01675 
01676   if (!acc) return gnc_numeric_zero ();
01677 
01678   xaccAccountSortSplits (acc, TRUE); /* just in case, normally a noop */
01679   xaccAccountRecomputeBalance (acc); /* just in case, normally a noop */
01680 
01681   balance = acc->balance;
01682 
01683   /* Since transaction post times are stored as a Timespec,
01684    * convert date into a Timespec as well rather than converting
01685    * each transaction's Timespec into a time_t.
01686    */
01687   ts.tv_sec = date;
01688   ts.tv_nsec = 0;
01689 
01690   lp = acc->splits;
01691   while( lp && !found )
01692   {
01693     xaccTransGetDatePostedTS( xaccSplitGetParent( (Split *)lp->data ),
01694                               &trans_ts );
01695     if( timespec_cmp( &trans_ts, &ts ) > 0 )
01696       found = TRUE;
01697     else
01698       lp = lp->next;
01699   }
01700 
01701   if( lp ) {
01702     if ( lp->prev ) {
01703       /* Since lp is now pointing to a split which was past the reconcile
01704        * date, get the running balance of the previous split.
01705        */
01706       balance = xaccSplitGetBalance( (Split *)lp->prev->data );
01707     }
01708     else {
01709       /* AsOf date must be before any entries, return zero. */
01710       balance = gnc_numeric_zero();
01711     }
01712   }
01713 
01714   /* Otherwise there were no splits posted after the given date,
01715    * so the latest account balance should be good enough.
01716    */
01717 
01718   return( balance );
01719 }
01720 
01721 /*
01722  * Originally gsr_account_present_balance in gnc-split-reg.c
01723  *
01724  * How does this routine compare to xaccAccountGetBalanceAsOfDate just
01725  * above?  These two routines should eventually be collapsed into one.
01726  * Perhaps the startup logic from that one, and the logic from this
01727  * one that walks from the tail of the split list.
01728  */
01729 gnc_numeric
01730 xaccAccountGetPresentBalance (Account *account)
01731 {
01732   GList *node;
01733   time_t today;
01734 
01735   if (!account)
01736     return gnc_numeric_zero ();
01737 
01738   today = gnc_timet_get_today_end();
01739   for (node = g_list_last (account->splits); node; node = node->prev)
01740   {
01741     Split *split = node->data;
01742 
01743     if (xaccTransGetDate (xaccSplitGetParent (split)) <= today)
01744       return xaccSplitGetBalance (split);
01745   }
01746 
01747   return gnc_numeric_zero ();
01748 }
01749 
01750 
01751 /********************************************************************\
01752 \********************************************************************/
01753 /* XXX TODO: These 'GetBal' routines should be moved to some
01754  * utility area outside of the core account engine area.
01755  */
01756 
01757 /*
01758  * Convert a balance from one currency to another.
01759  */
01760 gnc_numeric
01761 xaccAccountConvertBalanceToCurrency(Account *account, /* for book */
01762                                     gnc_numeric balance,
01763                                     gnc_commodity *balance_currency,
01764                                     gnc_commodity *new_currency)
01765 {
01766   QofBook *book;
01767   GNCPriceDB *pdb;
01768 
01769   if (gnc_numeric_zero_p (balance) ||
01770       gnc_commodity_equiv (balance_currency, new_currency))
01771     return balance;
01772 
01773   book = xaccGroupGetBook (xaccAccountGetRoot (account));
01774   pdb = gnc_pricedb_get_db (book);
01775 
01776   balance = gnc_pricedb_convert_balance_latest_price(pdb, balance, balance_currency, new_currency);
01777 
01778   return balance;
01779 }
01780 
01781 /*
01782  * Convert a balance from one currency to another with price of
01783  * a given date.
01784  */
01785 gnc_numeric
01786 xaccAccountConvertBalanceToCurrencyAsOfDate(Account *account, /* for book */
01787                                             gnc_numeric balance,
01788                                             gnc_commodity *balance_currency,
01789                                             gnc_commodity *new_currency,
01790                                             time_t date)
01791 {
01792   QofBook *book;
01793   GNCPriceDB *pdb;
01794   Timespec ts;
01795 
01796   if (gnc_numeric_zero_p (balance) ||
01797       gnc_commodity_equiv (balance_currency, new_currency))
01798     return balance;
01799 
01800   book = xaccGroupGetBook (xaccAccountGetRoot (account));
01801   pdb = gnc_book_get_pricedb (book);
01802 
01803   ts.tv_sec = date;
01804   ts.tv_nsec = 0;
01805 
01806   balance = gnc_pricedb_convert_balance_nearest_price(pdb, balance, balance_currency, new_currency, ts);
01807 
01808   return balance;
01809 }
01810 
01811 /*
01812  * Given an account and a GetBalanceFn pointer, extract the requested
01813  * balance from the account and then convert it to the desired
01814  * currency.
01815  */
01816 static gnc_numeric
01817 xaccAccountGetXxxBalanceInCurrency (Account *account,
01818                                     xaccGetBalanceFn fn,
01819                                     gnc_commodity *report_currency)
01820 {
01821   gnc_numeric balance;
01822 
01823   if (!account || !fn || !report_currency) return gnc_numeric_zero ();
01824   balance = fn(account);
01825   balance = xaccAccountConvertBalanceToCurrency(account, balance,
01826                                              account->commodity,
01827                                              report_currency);
01828   return balance;
01829 }
01830 
01831 /*
01832  * Data structure used to pass various arguments into the following fn.
01833  */
01834 typedef struct
01835 {
01836   gnc_commodity *currency;
01837   gnc_numeric balance;
01838   xaccGetBalanceFn fn;
01839 } CurrencyBalance;
01840 
01841 
01842 /*
01843  * A helper function for iterating over all the accounts in a list or
01844  * tree.  This function is called once per account, and sums up the
01845  * values of all these accounts.
01846  */
01847 static gpointer
01848 xaccAccountBalanceHelper (Account *account, gpointer data)
01849 {
01850   CurrencyBalance *cb = data;
01851   gnc_numeric balance;
01852 
01853   if (!cb->fn || !cb->currency)
01854     return NULL;
01855   balance = xaccAccountGetXxxBalanceInCurrency (account, cb->fn, cb->currency);
01856   cb->balance = gnc_numeric_add (cb->balance, balance,
01857                                  gnc_commodity_get_fraction (cb->currency),
01858                                  GNC_HOW_RND_ROUND);
01859   return NULL;
01860 }
01861 
01862 /*
01863  * Common function that iterates recursively over all accounts below
01864  * the specified account.  It uses the previous routine to sum up the
01865  * balances of all its children, and uses the specified function for
01866  * extracting the balance.  This function may extract the current
01867  * value, the reconciled value, etc.
01868  */
01869 static gnc_numeric
01870 xaccAccountGetXxxBalanceInCurrencyRecursive (Account *account,
01871                                              xaccGetBalanceFn fn,
01872                                              gnc_commodity *report_commodity,
01873                                              gboolean include_children)
01874 {
01875   gnc_numeric balance;
01876 
01877   if (account == NULL)
01878     return gnc_numeric_zero ();
01879   if (!report_commodity)
01880     report_commodity = xaccAccountGetCommodity (account);
01881   balance = xaccAccountGetXxxBalanceInCurrency (account, fn, report_commodity);
01882 
01883   /* If needed, sum up the children converting to *this* account's commodity. */
01884   if (include_children)
01885   {
01886     CurrencyBalance cb = { report_commodity, balance, fn };
01887 
01888     xaccGroupForEachAccount (account->children, xaccAccountBalanceHelper, &cb, TRUE);
01889     balance = cb.balance;
01890   }
01891 
01892   return balance;
01893 }
01894 
01895 gnc_numeric
01896 xaccAccountGetBalanceInCurrency (Account *account,
01897                                  gnc_commodity *report_commodity,
01898                                  gboolean include_children)
01899 {
01900   gnc_numeric rc;
01901   rc = xaccAccountGetXxxBalanceInCurrencyRecursive (account,
01902                    xaccAccountGetBalance,
01903                                                  report_commodity, include_children);
01904   PINFO (" baln=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, rc.num, rc.denom);
01905   return rc;
01906 }
01907 
01908 
01909 gnc_numeric
01910 xaccAccountGetClearedBalanceInCurrency (Account *account,
01911                                         gnc_commodity *report_commodity,
01912                                         gboolean include_children)
01913 {
01914   return
01915     xaccAccountGetXxxBalanceInCurrencyRecursive (account, xaccAccountGetClearedBalance,
01916                                                  report_commodity, include_children);
01917 }
01918 
01919 
01920 gnc_numeric
01921 xaccAccountGetReconciledBalanceInCurrency (Account *account,
01922                                  gnc_commodity *report_commodity,
01923                                  gboolean include_children)
01924 {
01925   return
01926     xaccAccountGetXxxBalanceInCurrencyRecursive (account, xaccAccountGetReconciledBalance,
01927                                                  report_commodity, include_children);
01928 }
01929 
01930 gnc_numeric
01931 xaccAccountGetPresentBalanceInCurrency (Account *account,
01932                                         gnc_commodity *report_commodity,
01933                                         gboolean include_children)
01934 {
01935   return
01936     xaccAccountGetXxxBalanceInCurrencyRecursive (account, xaccAccountGetPresentBalance,
01937                                                  report_commodity, include_children);
01938 }
01939 
01940 gnc_numeric
01941 xaccAccountGetProjectedMinimumBalanceInCurrency (Account *account,
01942                                                  gnc_commodity *report_commodity,
01943                                                  gboolean include_children)
01944 {
01945   return
01946     xaccAccountGetXxxBalanceInCurrencyRecursive (account, xaccAccountGetProjectedMinimumBalance,
01947                                                  report_commodity, include_children);
01948 }
01949 
01950 /********************************************************************\
01951 \********************************************************************/
01952 
01953 SplitList *
01954 xaccAccountGetSplitList (Account *acc)
01955 {
01956   if (!acc) return NULL;
01957   return (acc->splits);
01958 }
01959 
01960 LotList *
01961 xaccAccountGetLotList (Account *acc)
01962 {
01963   if (!acc) return NULL;
01964   return (acc->lots);
01965 }
01966 
01967 LotList *
01968 xaccAccountFindOpenLots (Account *acc,
01969                         gboolean (*match_func)(GNCLot *lot,
01970                                                gpointer user_data),
01971                         gpointer user_data, GCompareFunc sort_func)
01972 {
01973   GList *lot_list;
01974   GList *retval = NULL;
01975 
01976   if (!acc)
01977     return NULL;
01978 
01979   lot_list = xaccAccountGetLotList (acc);
01980   for ( ; lot_list ; lot_list = lot_list->next ) {
01981     GNCLot *lot = lot_list->data;
01982 
01983     /* If this lot is closed, then ignore it */
01984     if (gnc_lot_is_closed (lot))
01985       continue;
01986 
01987     if (match_func && !(match_func)(lot, user_data))
01988       continue;
01989 
01990     /* Ok, this is a valid lot.  Add it to our list of lots */
01991     if (sort_func)
01992       retval = g_list_insert_sorted (retval, lot, sort_func);
01993     else
01994       retval = g_list_prepend (retval, lot);
01995   }
01996 
01997   return retval;
01998 }
01999 
02000 gpointer
02001 xaccAccountForEachLot(Account *acc,
02002                               gpointer (*proc)(GNCLot *lot, void *data),
02003                               void *data)
02004 {
02005   LotList *node;
02006 
02007   if (!acc) return(NULL);
02008   if (!proc) return(NULL);
02009 
02010   for (node = acc->lots; node; node=node->next)
02011   {
02012     GNCLot *lot = node->data;
02013     gpointer result = proc (lot, data);
02014     if (result) return result;
02015   }
02016 
02017   return(NULL);
02018 }
02019 
02020 /********************************************************************\
02021 \********************************************************************/
02022 
02023 gboolean
02024 xaccAccountGetTaxRelated (Account *account)
02025 {
02026   KvpValue *kvp;
02027 
02028   if (!account)
02029     return FALSE;
02030 
02031   kvp = kvp_frame_get_slot (account->inst.kvp_data, "tax-related");
02032   if (!kvp)
02033     return FALSE;
02034 
02035   return kvp_value_get_gint64 (kvp);
02036 }
02037 
02038 void
02039 xaccAccountSetTaxRelated (Account *account, gboolean tax_related)
02040 {
02041   KvpValue *new_value;
02042 
02043   if (!account)
02044     return;
02045 
02046   if (tax_related)
02047     new_value = kvp_value_new_gint64 (tax_related);
02048   else
02049     new_value = NULL;
02050 
02051   xaccAccountBeginEdit (account);
02052   kvp_frame_set_slot_nc(account->inst.kvp_data, "tax-related", new_value);
02053 
02054   mark_account (account);
02055   account->inst.dirty = TRUE;
02056   xaccAccountCommitEdit (account);
02057 }
02058 
02059 const char *
02060 xaccAccountGetTaxUSCode (Account *account)
02061 {
02062   KvpValue *value;
02063 
02064   if (!account)
02065     return FALSE;
02066 
02067   value = kvp_frame_get_slot_path (account->inst.kvp_data, "tax-US", "code", NULL);
02068   if (!value)
02069     return NULL;
02070 
02071   return kvp_value_get_string (value);
02072 }
02073 
02074 void
02075 xaccAccountSetTaxUSCode (Account *account, const char *code)
02076 {
02077   if (!account) return;
02078 
02079   xaccAccountBeginEdit (account);
02080   kvp_frame_set_str (account->inst.kvp_data, "/tax-US/code", code);
02081 
02082   mark_account (account);
02083   account->inst.dirty = TRUE;
02084   xaccAccountCommitEdit (account);
02085 }
02086 
02087 const char *
02088 xaccAccountGetTaxUSPayerNameSource (Account *account)
02089 {
02090   if (!account) return NULL;
02091   return kvp_frame_get_string (account->inst.kvp_data, "/tax-US/payer-name-source");
02092 }
02093 
02094 void
02095 xaccAccountSetTaxUSPayerNameSource (Account *account, const char *source)
02096 {
02097   if (!account) return;
02098 
02099   xaccAccountBeginEdit (account);
02100   kvp_frame_set_str (account->inst.kvp_data, "/tax-US/payer-name-source", source);
02101 
02102   mark_account (account);
02103   account->inst.dirty = TRUE;
02104   xaccAccountCommitEdit (account);
02105 }
02106 
02107 /********************************************************************\
02108 \********************************************************************/
02109 
02110 gboolean
02111 xaccAccountGetPlaceholder (Account *account)
02112 {
02113   KvpValue *kvp;
02114   char *setting;
02115 
02116   if ( ( account )                                      &&
02117        ( kvp = kvp_frame_get_slot (account->inst.kvp_data, "placeholder" ) ) &&
02118        ( kvp_value_get_type (kvp) == KVP_TYPE_STRING ) &&
02119        ( setting = kvp_value_get_string(kvp) ) &&
02120        ( !strcmp( setting, "true" ) ) )
02121       return TRUE;
02122 
02123   return FALSE;
02124 }
02125 
02126 void
02127 xaccAccountSetPlaceholder (Account *account, gboolean option)
02128 {
02129   if (!account)
02130     return;
02131 
02132   xaccAccountBeginEdit (account);
02133   kvp_frame_set_slot_nc(account->inst.kvp_data, "placeholder",
02134                         kvp_value_new_string (option ? "true" : "false"));
02135 
02136   mark_account (account);
02137   account->inst.dirty = TRUE;
02138   xaccAccountCommitEdit (account);
02139 }
02140 
02141 GNCPlaceholderType
02142 xaccAccountGetDescendantPlaceholder (Account *account)
02143 {
02144   GList *descendants, *node;
02145 
02146   if (!account)
02147     return PLACEHOLDER_NONE;
02148 
02149   if (xaccAccountGetPlaceholder(account))
02150     return PLACEHOLDER_THIS;
02151 
02152   descendants = xaccGroupGetSubAccounts(account->children);
02153   node = g_list_first(descendants);
02154   for ( ; node ; node = g_list_next(node) )
02155   {
02156     account = (Account *)node->data;
02157     if (xaccAccountGetPlaceholder(account)) return(PLACEHOLDER_CHILD);
02158   }
02159 
02160   return PLACEHOLDER_NONE;
02161 }
02162 
02163 /********************************************************************\
02164 \********************************************************************/
02165 
02166 gboolean
02167 xaccAccountHasAncestor (Account *account, Account * ancestor)
02168 {
02169   Account *parent;
02170 
02171   if ((account == NULL) || (ancestor == NULL))
02172     return FALSE;
02173 
02174   parent = xaccAccountGetParentAccount(account);
02175   while (parent != NULL)
02176   {
02177     if (parent == ancestor)
02178       return TRUE;
02179 
02180     parent = xaccAccountGetParentAccount(parent);
02181   }
02182 
02183   return FALSE;
02184 }
02185 
02186 /********************************************************************\
02187 \********************************************************************/
02188 
02189 /* You must edit the functions in this block in tandem.  KEEP THEM IN
02190    SYNC! */
02191 
02192 #define GNC_RETURN_ENUM_AS_STRING(x) case (x): return #x;
02193 
02194 const char *
02195 xaccAccountTypeEnumAsString(GNCAccountType type)
02196 {
02197   switch(type)
02198   {
02199     GNC_RETURN_ENUM_AS_STRING(NO_TYPE);
02200     GNC_RETURN_ENUM_AS_STRING(BANK);
02201     GNC_RETURN_ENUM_AS_STRING(CASH);
02202     GNC_RETURN_ENUM_AS_STRING(CREDIT);
02203     GNC_RETURN_ENUM_AS_STRING(ASSET);
02204     GNC_RETURN_ENUM_AS_STRING(LIABILITY);
02205     GNC_RETURN_ENUM_AS_STRING(STOCK);
02206     GNC_RETURN_ENUM_AS_STRING(MUTUAL);
02207     GNC_RETURN_ENUM_AS_STRING(CURRENCY);
02208     GNC_RETURN_ENUM_AS_STRING(INCOME);
02209     GNC_RETURN_ENUM_AS_STRING(EXPENSE);
02210     GNC_RETURN_ENUM_AS_STRING(EQUITY);
02211     GNC_RETURN_ENUM_AS_STRING(RECEIVABLE);
02212     GNC_RETURN_ENUM_AS_STRING(PAYABLE);
02213     GNC_RETURN_ENUM_AS_STRING(CHECKING);
02214     GNC_RETURN_ENUM_AS_STRING(SAVINGS);
02215     GNC_RETURN_ENUM_AS_STRING(MONEYMRKT);
02216     GNC_RETURN_ENUM_AS_STRING(CREDITLINE);
02217     default:
02218       PERR ("asked to translate unknown account type %d.\n", type);
02219       break;
02220   }
02221   return(NULL);
02222 }
02223 
02224 #undef GNC_RETURN_ENUM_AS_STRING
02225 
02226 #define GNC_RETURN_ON_MATCH(x) \
02227   if(safe_strcmp(#x, (str)) == 0) { *type = x; return(TRUE); }
02228 
02229 gboolean
02230 xaccAccountStringToType(const char* str, GNCAccountType *type)
02231 {
02232 
02233   GNC_RETURN_ON_MATCH(NO_TYPE);
02234   GNC_RETURN_ON_MATCH(BANK);
02235   GNC_RETURN_ON_MATCH(CASH);
02236   GNC_RETURN_ON_MATCH(CREDIT);
02237   GNC_RETURN_ON_MATCH(ASSET);
02238   GNC_RETURN_ON_MATCH(LIABILITY);
02239   GNC_RETURN_ON_MATCH(STOCK);
02240   GNC_RETURN_ON_MATCH(MUTUAL);
02241   GNC_RETURN_ON_MATCH(CURRENCY);
02242   GNC_RETURN_ON_MATCH(INCOME);
02243   GNC_RETURN_ON_MATCH(EXPENSE);
02244   GNC_RETURN_ON_MATCH(EQUITY);
02245   GNC_RETURN_ON_MATCH(RECEIVABLE);
02246   GNC_RETURN_ON_MATCH(PAYABLE);
02247   GNC_RETURN_ON_MATCH(CHECKING);
02248   GNC_RETURN_ON_MATCH(SAVINGS);
02249   GNC_RETURN_ON_MATCH(MONEYMRKT);
02250   GNC_RETURN_ON_MATCH(CREDITLINE);
02251 
02252   PERR("asked to translate unknown account type string %s.\n",
02253        str ? str : "(null)");
02254 
02255   return(FALSE);
02256 }
02257 
02258 #undef GNC_RETURN_ON_MATCH
02259 
02260 /* impedance mismatch is a source of loss */
02261 GNCAccountType
02262 xaccAccountStringToEnum(const char* str)
02263 {
02264   GNCAccountType type;
02265   gboolean rc;
02266   rc = xaccAccountStringToType(str, &type);
02267   if (FALSE == rc) return BAD_TYPE;
02268   return type;
02269 }
02270 
02271 /********************************************************************\
02272 \********************************************************************/
02273 
02274 static char *
02275 account_type_name[NUM_ACCOUNT_TYPES] = {
02276   N_("Bank"),
02277   N_("Cash"),
02278   N_("Asset"),
02279   N_("Credit Card"),
02280   N_("Liability"),
02281   N_("Stock"),
02282   N_("Mutual Fund"),
02283   N_("Currency"),
02284   N_("Income"),
02285   N_("Expense"),
02286   N_("Equity"),
02287   N_("A/Receivable"),
02288   N_("A/Payable")
02289   /*
02290     N_("Checking"),
02291     N_("Savings"),
02292     N_("Money Market"),
02293     N_("Credit Line")
02294   */
02295 };
02296 
02297 const char *
02298 xaccAccountGetTypeStr(GNCAccountType type) {
02299   if (0 > type) return "";
02300   if (NUM_ACCOUNT_TYPES <= type) return "";
02301   return _(account_type_name [type]);
02302 }
02303 
02304 GNCAccountType
02305 xaccAccountGetTypeFromStr (const gchar *str)
02306 {
02307   gint type;
02308 
02309   for (type = 0; type < NUM_ACCOUNT_TYPES; type++)
02310   {
02311     if (!safe_strcmp (str, _(account_type_name [type])))
02312       return type;
02313   }
02314 
02315   PERR("asked to translate unknown account type string %s.\n",
02316        str ? str : "(null)");
02317 
02318   return BAD_TYPE;
02319 }
02320 
02321 
02322 /********************************************************************\
02323 \********************************************************************/
02324 
02325 gboolean
02326 xaccAccountTypesCompatible (GNCAccountType parent_type,
02327                             GNCAccountType child_type)
02328 {
02329   gboolean compatible = FALSE;
02330 
02331   switch(parent_type)
02332   {
02333     case BANK:
02334     case CASH:
02335     case ASSET:
02336     case STOCK:
02337     case MUTUAL:
02338     case CURRENCY:
02339     case CREDIT:
02340     case LIABILITY:
02341     case RECEIVABLE:
02342     case PAYABLE:
02343       compatible = ((child_type == BANK)     ||
02344                     (child_type == CASH)     ||
02345                     (child_type == ASSET)    ||
02346                     (child_type == STOCK)    ||
02347                     (child_type == MUTUAL)   ||
02348                     (child_type == CURRENCY) ||
02349                     (child_type == CREDIT)   ||
02350                     (child_type == LIABILITY)||
02351                     (child_type == RECEIVABLE)||
02352                     (child_type == PAYABLE));
02353       break;
02354     case INCOME:
02355     case EXPENSE:
02356       compatible = ((child_type == INCOME) ||
02357                     (child_type == EXPENSE));
02358       break;
02359     case EQUITY:
02360       compatible = (child_type == EQUITY);
02361       break;
02362     default:
02363       PERR("bad account type: %d", parent_type);
02364       break;
02365   }
02366 
02367   return compatible;
02368 }
02369 
02370 /********************************************************************\
02371 \********************************************************************/
02372 
02373 gboolean
02374 xaccAccountGetReconcileLastDate (Account *account, time_t *last_date)
02375 {
02376   KvpValue *value;
02377 
02378   if (!account)
02379     return FALSE;
02380 
02381   value = kvp_frame_get_slot_path (account->inst.kvp_data,
02382                                    "reconcile-info", "last-date", NULL);
02383   if (!value)
02384     return FALSE;
02385 
02386   if (kvp_value_get_type (value) == KVP_TYPE_GINT64)
02387   {
02388     if (last_date)
02389       *last_date = kvp_value_get_gint64 (value);
02390 
02391     return TRUE;
02392   }
02393 
02394   return FALSE;
02395 }
02396 
02397 /********************************************************************\
02398 \********************************************************************/
02399 
02400 void
02401 xaccAccountSetReconcileLastDate (Account *account, time_t last_date)
02402 {
02403   if (!account) return;
02404 
02405   xaccAccountBeginEdit (account);
02406   kvp_frame_set_gint64 (account->inst.kvp_data,
02407                  "/reconcile-info/last-date", last_date);
02408 
02409   mark_account (account);
02410   account->inst.dirty = TRUE;
02411   xaccAccountCommitEdit (account);
02412 }
02413 
02414 /********************************************************************\
02415 \********************************************************************/
02416 
02417 gboolean
02418 xaccAccountGetReconcileLastInterval (Account *account, int *months, int *days)
02419 {
02420   KvpValue *value1, *value2;
02421 
02422   if (!account)
02423     return FALSE;
02424 
02425   value1 = kvp_frame_get_slot_path (account->inst.kvp_data, "reconcile-info",
02426                                     "last-interval", "months", NULL);
02427   value2 = kvp_frame_get_slot_path (account->inst.kvp_data, "reconcile-info",
02428                                     "last-interval", "days", NULL);
02429   if (!value1 || (kvp_value_get_type (value1) != KVP_TYPE_GINT64) ||
02430       !value2 || (kvp_value_get_type (value2) != KVP_TYPE_GINT64))
02431     return FALSE;
02432 
02433   if (months)
02434     *months = kvp_value_get_gint64 (value1);
02435   if (days)
02436     *days = kvp_value_get_gint64 (value2);
02437   return TRUE;
02438 }
02439 
02440 /********************************************************************\
02441 \********************************************************************/
02442 
02443 void
02444 xaccAccountSetReconcileLastInterval (Account *account, int months, int days)
02445 {
02446   KvpFrame *frame;
02447   if (!account) return;
02448 
02449   xaccAccountBeginEdit (account);
02450 
02451   frame = kvp_frame_get_frame_slash (account->inst.kvp_data,
02452          "/reconcile-info/last-interval");
02453   g_assert(frame);
02454 
02455   kvp_frame_set_gint64 (frame, "months", months);
02456   kvp_frame_set_gint64 (frame, "days", days);
02457 
02458   mark_account (account);
02459   account->inst.dirty = TRUE;
02460   xaccAccountCommitEdit (account);
02461 }
02462 
02463 /********************************************************************\
02464 \********************************************************************/
02465 
02466 gboolean
02467 xaccAccountGetReconcilePostponeDate (Account *account,
02468                                      time_t *postpone_date)
02469 {
02470   KvpValue *value;
02471 
02472   if (!account)
02473     return FALSE;
02474 
02475   value = kvp_frame_get_slot_path (account->inst.kvp_data,
02476                                    "reconcile-info", "postpone", "date", NULL);
02477   if (!value)
02478     return FALSE;
02479 
02480   if (kvp_value_get_type (value) == KVP_TYPE_GINT64)
02481   {
02482     if (postpone_date)
02483       *postpone_date = kvp_value_get_gint64 (value);
02484 
02485     return TRUE;
02486   }
02487 
02488   return FALSE;
02489 }
02490 
02491 /********************************************************************\
02492 \********************************************************************/
02493 
02494 void
02495 xaccAccountSetReconcilePostponeDate (Account *account,
02496                                      time_t postpone_date)
02497 {
02498   if (!account) return;
02499 
02500   xaccAccountBeginEdit (account);
02501 
02502   /* XXX this should be using timespecs, not gints !! */
02503   kvp_frame_set_gint64 (account->inst.kvp_data,
02504             "/reconcile-info/postpone/date", postpone_date);
02505 
02506   mark_account (account);
02507   account->inst.dirty = TRUE;
02508   xaccAccountCommitEdit (account);
02509 }
02510 
02511 /********************************************************************\
02512 \********************************************************************/
02513 
02514 gboolean
02515 xaccAccountGetReconcilePostponeBalance (Account *account,
02516                                         gnc_numeric *balance)
02517 {
02518   KvpValue *value;
02519 
02520   if (!account)
02521     return FALSE;
02522 
02523   value = kvp_frame_get_slot_path (account->inst.kvp_data,
02524                                    "reconcile-info", "postpone", "balance",
02525                                    NULL);
02526   if (!value)
02527     return FALSE;
02528 
02529   if (kvp_value_get_type (value) == KVP_TYPE_NUMERIC)
02530   {
02531     if (balance)
02532       *balance = kvp_value_get_numeric (value);
02533 
02534     return TRUE;
02535   }
02536 
02537   return FALSE;
02538 }
02539 
02540 /********************************************************************\
02541 \********************************************************************/
02542 
02543 void
02544 xaccAccountSetReconcilePostponeBalance (Account *account,
02545                                         gnc_numeric balance)
02546 {
02547   if (!account) return;
02548 
02549   xaccAccountBeginEdit (account);
02550   kvp_frame_set_gnc_numeric (account->inst.kvp_data,
02551            "/reconcile-info/postpone/balance", balance);
02552 
02553   mark_account (account);
02554   account->inst.dirty = TRUE;
02555   xaccAccountCommitEdit (account);
02556 }
02557 
02558 /********************************************************************\
02559 
02560 \********************************************************************/
02561 
02562 void
02563 xaccAccountClearReconcilePostpone (Account *account)
02564 {
02565   if (!account)
02566     return;
02567 
02568   xaccAccountBeginEdit (account);
02569   {
02570     kvp_frame_set_slot_path (account->inst.kvp_data, NULL,
02571                              "reconcile-info", "postpone", NULL);
02572 
02573     mark_account (account);
02574   }
02575   account->inst.dirty = TRUE;
02576   xaccAccountCommitEdit (account);
02577 }
02578 
02579 /********************************************************************\
02580 \********************************************************************/
02581 
02582 /* xaccAccountGetAutoInterestXfer: determine whether the auto interest
02583  * xfer option is enabled for this account, and return that value.
02584  * If it is not defined for the account, return the default value.
02585  */
02586 gboolean
02587 xaccAccountGetAutoInterestXfer (Account *account, gboolean default_value)
02588 {
02589   KvpValue *value = NULL;
02590   char *setting = NULL;
02591   gboolean result = default_value;
02592 
02593   if ( ( account )                                      &&
02594        ( value = kvp_frame_get_slot_path (account->inst.kvp_data,
02595                                           "reconcile-info",
02596                                           "auto-interest-transfer",
02597                                           NULL) )        &&
02598        ( kvp_value_get_type (value) == KVP_TYPE_STRING ) &&
02599        ( setting = kvp_value_get_string(value) ) )
02600   {
02601     if( !strcmp( setting, "true" ) )
02602       result = TRUE;
02603     else if( !strcmp( setting, "false" ) )
02604       result = FALSE;
02605   }
02606 
02607   return (result);
02608 }
02609 
02610 /********************************************************************\
02611 \********************************************************************/
02612 
02613 void
02614 xaccAccountSetAutoInterestXfer (Account *account, gboolean option)
02615 {
02616   if (!account)
02617     return;
02618 
02619   xaccAccountBeginEdit (account);
02620 
02621   /* FIXME: need KVP_TYPE_BOOLEAN for this someday */
02622   kvp_frame_set_str (account->inst.kvp_data,
02623        "/reconcile-info/auto-interest-transfer",
02624        (option ? "true" : "false"));
02625 
02626   mark_account (account);
02627   account->inst.dirty = TRUE;
02628   xaccAccountCommitEdit (account);
02629 }
02630 
02631 /********************************************************************\
02632 \********************************************************************/
02633 
02634 const char *
02635 xaccAccountGetLastNum (Account *account)
02636 {
02637   KvpValue *value;
02638 
02639   if (!account)
02640     return FALSE;
02641 
02642   value = kvp_frame_get_slot (account->inst.kvp_data, "last-num");
02643   if (!value)
02644     return FALSE;
02645 
02646   return kvp_value_get_string (value);
02647 }
02648 
02649 /********************************************************************\
02650 \********************************************************************/
02651 
02652 void
02653 xaccAccountSetLastNum (Account *account, const char *num)
02654 {
02655   if (!account)
02656     return;
02657 
02658   xaccAccountBeginEdit (account);
02659   kvp_frame_set_slot_nc (account->inst.kvp_data, "last-num",
02660                                             kvp_value_new_string (num));
02661   mark_account (account);
02662   account->inst.dirty = TRUE;
02663   xaccAccountCommitEdit (account);
02664 }
02665 
02666 /********************************************************************\
02667 \********************************************************************/
02668 
02669 void
02670 dxaccAccountSetPriceSrc(Account *acc, const char *src)
02671 {
02672   if(!acc) return;
02673 
02674   xaccAccountBeginEdit(acc);
02675   {
02676     GNCAccountType t = acc->type;
02677 
02678     if((t == STOCK) || (t == MUTUAL) || (t == CURRENCY)) {
02679       kvp_frame_set_slot_nc(acc->inst.kvp_data,
02680                             "old-price-source",
02681                             src ? kvp_value_new_string(src) : NULL);
02682       mark_account (acc);
02683     }
02684   }
02685   acc->inst.dirty = TRUE;
02686   xaccAccountCommitEdit(acc);
02687 }
02688 
02689 /********************************************************************\
02690 \********************************************************************/
02691 
02692 const char*
02693 dxaccAccountGetPriceSrc(Account *acc)
02694 {
02695   GNCAccountType t;
02696   if(!acc) return NULL;
02697 
02698   t = acc->type;
02699   if((t == STOCK) || (t == MUTUAL) || (t == CURRENCY))
02700   {
02701     KvpValue *value = kvp_frame_get_slot(acc->inst.kvp_data, "old-price-source");
02702     if(value) return (kvp_value_get_string(value));
02703   }
02704   return NULL;
02705 }
02706 
02707 /********************************************************************\
02708 \********************************************************************/
02709 
02710 void
02711 dxaccAccountSetQuoteTZ(Account *acc, const char *tz)
02712 {
02713   if(!acc) return;
02714 
02715   xaccAccountBeginEdit(acc);
02716   {
02717     GNCAccountType t = acc->type;
02718 
02719     if((t == STOCK) || (t == MUTUAL) || (t == CURRENCY)) {
02720       kvp_frame_set_slot_nc(acc->inst.kvp_data,
02721                             "old-quote-tz",
02722                             tz ? kvp_value_new_string(tz) : NULL);
02723       mark_account (acc);
02724     }
02725   }
02726   acc->inst.dirty = TRUE;
02727   xaccAccountCommitEdit(acc);
02728 }
02729 
02730 /********************************************************************\
02731 \********************************************************************/
02732 
02733 const char*
02734 dxaccAccountGetQuoteTZ(Account *acc)
02735 {
02736   GNCAccountType t;
02737   if(!acc) return NULL;
02738 
02739   t = acc->type;
02740   if((t == STOCK) || (t == MUTUAL) || (t == CURRENCY))
02741   {
02742     KvpValue *value = kvp_frame_get_slot(acc->inst.kvp_data, "old-quote-tz");
02743     if(value) return (kvp_value_get_string(value));
02744   }
02745   return NULL;
02746 }
02747 
02748 /********************************************************************\
02749 \********************************************************************/
02750 
02751 void
02752 xaccAccountSetReconcileChildrenStatus(Account *account, gboolean status)
02753 {
02754   if (!account) return;
02755 
02756   xaccAccountBeginEdit (account);
02757 
02758   /* XXX FIXME: someday this should use KVP_TYPE_BOOLEAN */
02759   kvp_frame_set_gint64 (account->inst.kvp_data,
02760         "/reconcile-info/include-children", status);
02761 
02762   account->inst.dirty = TRUE;
02763   xaccAccountCommitEdit (account);
02764 }
02765 
02766 /********************************************************************\
02767 \********************************************************************/
02768 
02769 gboolean
02770 xaccAccountGetReconcileChildrenStatus(Account *account)
02771 {
02772   KvpValue *status;
02773   if (!account)
02774     return FALSE;
02775   /* access the account's kvp-data for status and return that, if no value
02776    * is found then we can assume not to include the children, that being
02777    * the default behaviour
02778    */
02779   status = kvp_frame_get_slot_path (account->inst.kvp_data,
02780                                     "reconcile-info",
02781                                     "include-children",
02782                                     NULL);
02783   if (!status)
02784     return FALSE;
02785   return kvp_value_get_gint64 (status);
02786 }
02787 
02788 /********************************************************************\
02789 \********************************************************************/
02790 
02791 gint
02792 xaccAccountForEachTransaction(Account *acc,
02793                               TransactionCallback proc,
02794                               void *data)
02795 {
02796   if(!acc || !proc) return 0;
02797   xaccAccountBeginStagedTransactionTraversals (acc);
02798   return xaccAccountStagedTransactionTraversal(acc, 42, proc, data);
02799 }
02800 
02801 /********************************************************************\
02802 \********************************************************************/
02803 
02804 /* The caller of this function can get back one or both of the
02805  * matching split and transaction pointers, depending on whether
02806  * a valid pointer to the location to store those pointers is
02807  * passed.
02808  */
02809 static void
02810 finder_help_function(Account *account,
02811                      const char *description,
02812                      Split **split,
02813                      Transaction **trans )
02814 {
02815   GList *slp;
02816 
02817   /* First, make sure we set the data to NULL BEFORE we start */
02818   if (split) *split = NULL;
02819   if (trans) *trans = NULL;
02820 
02821   /* Then see if we have any work to do */
02822   if (account == NULL) return;
02823 
02824   /* Why is this loop iterated backwards ?? Presumably because the split
02825    * list is in date order, and the most recent matches should be
02826    * returned!?  */
02827   for (slp = g_list_last (account->splits);
02828        slp;
02829        slp = slp->prev)
02830   {
02831     Split *lsplit = slp->data;
02832     Transaction *ltrans = xaccSplitGetParent(lsplit);
02833 
02834     if (safe_strcmp (description, xaccTransGetDescription (ltrans)) == 0)
02835     {
02836       if( split ) *split = lsplit;
02837       if( trans ) *trans = ltrans;
02838       return;
02839     }
02840   }
02841 }
02842 
02843 Split *
02844 xaccAccountFindSplitByDesc(Account *account, const char *description)
02845 {
02846   Split *split;
02847 
02848   /* Get the split which has a transaction matching the description. */
02849   finder_help_function(account, description, &split, NULL );
02850 
02851   return( split );
02852 }
02853 
02854 /* This routine is for finding a matching transaction in an account by
02855  * matching on the description field. This routine is used for auto-filling
02856  * in registers with a default leading account. The dest_trans is a
02857  * transaction used for currency checking. */
02858 Transaction *
02859 xaccAccountFindTransByDesc(Account *account, const char *description)
02860 {
02861   Transaction *trans;
02862 
02863   /* Get the transation matching the description. */
02864   finder_help_function(account, description, NULL, &trans );
02865 
02866   return( trans );
02867 }
02868 
02869 /* ================================================================ */
02870 /* QofObject function implementation and registration */
02871 
02872 static QofObject account_object_def = {
02873   interface_version:     QOF_OBJECT_VERSION,
02874   e_type:                GNC_ID_ACCOUNT,
02875   type_label:            "Account",
02876   create:                (gpointer)xaccMallocAccount,
02877   book_begin:            NULL,
02878   book_end:              NULL,
02879   is_dirty:              NULL,
02880   mark_clean:            NULL,
02881   foreach:               qof_collection_foreach,
02882   printable:             (const char* (*)(gpointer)) xaccAccountGetName,
02883   version_cmp:           (int (*)(gpointer,gpointer)) qof_instance_version_cmp,
02884 };
02885 
02886 gboolean xaccAccountRegister (void)
02887 {
02888   static QofParam params[] = {
02889     { ACCOUNT_NAME_,                    QOF_TYPE_STRING, (QofAccessFunc)xaccAccountGetName,                     (QofSetterFunc) xaccAccountSetName },
02890     { ACCOUNT_CODE_,                    QOF_TYPE_STRING, (QofAccessFunc)xaccAccountGetCode,                     (QofSetterFunc) xaccAccountSetCode },
02891     { ACCOUNT_DESCRIPTION_,     QOF_TYPE_STRING, (QofAccessFunc)xaccAccountGetDescription,              (QofSetterFunc) xaccAccountSetDescription },
02892     { ACCOUNT_NOTES_,                   QOF_TYPE_STRING,  (QofAccessFunc)xaccAccountGetNotes,                   (QofSetterFunc) xaccAccountSetNotes },
02893     { ACCOUNT_PRESENT_,                 QOF_TYPE_NUMERIC, (QofAccessFunc)xaccAccountGetPresentBalance,  NULL },
02894     { ACCOUNT_BALANCE_,                 QOF_TYPE_NUMERIC, (QofAccessFunc)xaccAccountGetBalance,                 NULL },
02895     { ACCOUNT_CLEARED_,                 QOF_TYPE_NUMERIC, (QofAccessFunc)xaccAccountGetClearedBalance,  NULL },
02896     { ACCOUNT_RECONCILED_,              QOF_TYPE_NUMERIC, (QofAccessFunc)xaccAccountGetReconciledBalance, NULL },
02897    { ACCOUNT_TYPE_,          QOF_TYPE_STRING,  (QofAccessFunc)qofAccountGetTypeString,   (QofSetterFunc)qofAccountSetType },
02898    { ACCOUNT_FUTURE_MINIMUM_,QOF_TYPE_NUMERIC, (QofAccessFunc)xaccAccountGetProjectedMinimumBalance, NULL },
02899     { ACCOUNT_TAX_RELATED,              QOF_TYPE_BOOLEAN, (QofAccessFunc)xaccAccountGetTaxRelated,              (QofSetterFunc) xaccAccountSetTaxRelated },
02900    { ACCOUNT_SCU,            QOF_TYPE_INT32,   (QofAccessFunc)xaccAccountGetCommoditySCU,(QofSetterFunc)xaccAccountSetCommoditySCU },
02901         { ACCOUNT_NSCU,                         QOF_TYPE_BOOLEAN, (QofAccessFunc)xaccAccountGetNonStdSCU,               (QofSetterFunc)xaccAccountSetNonStdSCU },
02902    { ACCOUNT_PARENT,         GNC_ID_ACCOUNT,   (QofAccessFunc)xaccAccountGetParentAccount,(QofSetterFunc)qofAccountSetParent },
02903     { QOF_PARAM_BOOK,                   QOF_ID_BOOK,      (QofAccessFunc)qof_instance_get_book,                 NULL },
02904     { QOF_PARAM_GUID,                   QOF_TYPE_GUID,    (QofAccessFunc)qof_instance_get_guid,                 NULL },
02905     { ACCOUNT_KVP,                              QOF_TYPE_KVP,     (QofAccessFunc)qof_instance_get_slots,                NULL },
02906     { NULL },
02907   };
02908 
02909   qof_class_register (GNC_ID_ACCOUNT, (QofSortFunc)xaccAccountOrder, params);
02910 
02911   return qof_object_register (&account_object_def);
02912 }
02913 
02914 /* ======================= END OF FILE =========================== */

Generated on Sun Sep 4 18:07:33 2005 for GnuCash by  doxygen 1.4.3-20050530