GnuCash 2.4.99
Transaction.c
00001 /********************************************************************\
00002  * Transaction.c -- transaction implementation                      *
00003  * Copyright (C) 1997 Robin D. Clark                                *
00004  * Copyright (C) 1997-2003 Linas Vepstas <linas@linas.org>          *
00005  * Copyright (C) 2000 Bill Gribble <grib@billgribble.com>           *
00006  * Copyright (c) 2006 David Hampton <hampton@employees.org>         *
00007  *                                                                  *
00008  * This program is free software; you can redistribute it and/or    *
00009  * modify it under the terms of the GNU General Public License as   *
00010  * published by the Free Software Foundation; either version 2 of   *
00011  * the License, or (at your option) any later version.              *
00012  *                                                                  *
00013  * This program is distributed in the hope that it will be useful,  *
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
00016  * GNU General Public License for more details.                     *
00017  *                                                                  *
00018  * You should have received a copy of the GNU General Public License*
00019  * along with this program; if not, contact:                        *
00020  *                                                                  *
00021  * Free Software Foundation           Voice:  +1-617-542-5942       *
00022  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
00023  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
00024  *                                                                  *
00025 \********************************************************************/
00026 
00027 #include "config.h"
00028 
00029 #include <glib.h>
00030 #include <glib/gi18n.h>
00031 #include <stdlib.h>
00032 #include <string.h>
00033 #ifdef HAVE_SYS_TIME_H
00034 # include <sys/time.h>
00035 #else
00036 /* We simply define the struct timeval on our own here. */
00037 struct timeval
00038 {
00039     long    tv_sec;         /* seconds */
00040     long    tv_usec;        /* and microseconds */
00041 };
00042 /* include <Winsock2.h> */
00043 #endif
00044 #include <time.h>
00045 #ifdef HAVE_UNISTD_H
00046 # include <unistd.h>
00047 #endif
00048 
00049 #include "AccountP.h"
00050 #include "Scrub.h"
00051 #include "Scrub3.h"
00052 #include "TransactionP.h"
00053 #include "SplitP.h"
00054 #include "TransLog.h"
00055 #include "cap-gains.h"
00056 #include "gnc-commodity.h"
00057 #include "gnc-engine.h"
00058 #include "gnc-lot.h"
00059 #include "gnc-event.h"
00060 
00061 #include "qofbackend-p.h"
00062 
00063 /* Notes about xaccTransBeginEdit(), xaccTransCommitEdit(), and
00064  *  xaccTransRollback():
00065  *
00066  * Why use it:
00067  *
00068  *   Data consistency: Wrapping your changes to financial data inside
00069  *   a BeginEdit/CommitEdit block allows the engine to verify that
00070  *   your changes still leave the financial objects in an internally
00071  *   consistent state.  This is true even though you may make a series
00072  *   of individual changes that are not consistent by themselves.  In
00073  *   this way, it's like telling the engine, "Okay, I've finished my
00074  *   edits.  Please check my work."
00075  *
00076  *   Data integrity: The other benefit of the BeginEdit/CommitEdit
00077  *   block is that it allows the engine (and the backend) to remember
00078  *   the last known correct state of your data.  This allows you to
00079  *   undo any changes that you don't want to keep.  In this way, it's
00080  *   like telling the engine telling the back end, "Yes, I really mean
00081  *   it.  Remember this data." or "Nevermind, scratch that."  The
00082  *   important feature here is that if things go bad, for whatever
00083  *   reason (e.g. the application crashed, you lost the backend), your
00084  *   data remains in the state it was in just after the previous
00085  *   xaccTransCommitEdit().  [assuming no nesting, which probably
00086  *   isn't useful outside the engine.]
00087  *
00088  *   Note that the backend doesn't care about data consistency -
00089  *   that's the engine's job.
00090  *
00091  * Example Use:
00092  *
00093  *   xaccTransBeginEdit(trans);
00094  *
00095  *
00096  *   split = xaccMallocSplit(book);
00097  *   xaccSplitSetAccount(split, acc);
00098  *   xaccSplitSetParent(split, trans);  // Adding a new split
00099  *
00100  *   xaccSplitSetValue(split, val);     // Changing a split
00101  *
00102  *   xaccSplitDestroy(split);           // Removing a split
00103  *
00104  *   xaccTransSetNum(trans, "501");     // Changing the trans
00105  *
00106  *   if (really_do_it)
00107  *      xaccTransCommitEdit(trans);
00108  *   else
00109  *      xaccTransRollbackEdit(trans);
00110  *
00111  * How it works:
00112  *
00113  *   Calling xaccTransBeginEdit() starts a BeginEdit/CommitEdit block.
00114  *   Inside the block any changes to the transaction or any splits in
00115  *   the transaction are considered "pending".  What does that mean?
00116  *
00117  *   In general that means that if you set and then get the
00118  *   transaction's or split's parameters inside the
00119  *   BeginEdit/CommitEdit block, you'll get the values you just set.
00120  *   However, if you change an object's many-to-one relationship with
00121  *   another object, you won't see the change from the "many" side
00122  *   until the CommitEdit.  For example, if you move a split from one
00123  *   account into another, you can see the change with
00124  *   xaccSplitGetAccount(), but both Accounts' split lists won't be
00125  *   updated until the CommitEdit.  Correspondingly, no signals
00126  *   (events) will be generated for those "foreign" objects, or the
00127  *   Transaction, until the CommitEdit.
00128  *
00129  *   This behavior is important because, when we're finally ready to
00130  *   commit to the backend, we can't be 100% sure that the backend
00131  *   will still be available.  We have to offer the backend all of the
00132  *   new state as if it were already "true", but we need to save all of
00133  *   the old state in case the backend won't accept our commit.  If
00134  *   the backend commit fails, we have to restore all the old state.
00135  *   If the backend commit succeeds, and *only* after it succeeds, we
00136  *   can advertise the new state to the rest of the engine (and gui).
00137  *
00138  *  Q: Who owns the ref of an added split if the Transaction is rolled
00139  *  back?
00140  *
00141  *  A: This is a design decision.  If the answer is 'the user',
00142  *  then the burden is on the api user to check the transaction after
00143  *  every commit to see if the added split is really in the
00144  *  transaction.  If they don't they risk leaking the split if the
00145  *  commit was rolled back.  Another design is to answer 'the engine'.
00146  *  In that case the burden is on the engine to free a newly added
00147  *  split if the commit is rolled back.  Unfortunately the engine
00148  *  objects aren't ref-counted, so this is tricky.
00149  *
00150  *  In the current implementation, the answer is 'the engine', but
00151  *  that means that you must not add the split to two different
00152  *  transactions during the begin/commit block, because if one rolls
00153  *  back, they will both think they own the split.  This is one
00154  *  specific example of the general problem that the outcome of two
00155  *  parallel begin/commit edit blocks for two transactions where edits
00156  *  for both transactions involve the same splits and one or more
00157  *  edit-blocks is rolled-back, is poorly-defined.
00158  *
00159  *
00160  *
00161  * Design notes on event-generation: transaction-modified-events
00162  * should not be generated until transaction commit or rollback
00163  * time.  They should not be generated as each field is tweaked.
00164  * This for two reasons:
00165  * 1) Most editing events make multiple changes to a transaction,
00166  *    which would generate a flurry of (needless) events, if they
00167  *    weren't saved up till the commit.
00168  * 2) Technically, its incorrect to use transaction data
00169  *    until the transaction is commited.  The GUI element that
00170  *    is changing the data can look at it, but all of the rest
00171  *    of the GUI should ignore the data until its commited.
00172  */
00173 
00174 const char *trans_notes_str = "notes";
00175 const char *void_reason_str = "void-reason";
00176 const char *void_time_str = "void-time";
00177 const char *void_former_notes_str = "void-former-notes";
00178 const char *trans_is_closing_str = "book_closing";
00179 
00180 /* KVP entry for date-due value */
00181 #define TRANS_DATE_DUE_KVP       "trans-date-due"
00182 #define TRANS_TXN_TYPE_KVP       "trans-txn-type"
00183 #define TRANS_READ_ONLY_REASON   "trans-read-only"
00184 #define TRANS_REVERSED_BY        "reversed-by"
00185 
00186 #define ISO_DATELENGTH 32 /* length of an iso 8601 date string. */
00187 
00188 /* This static indicates the debugging module that this .o belongs to.  */
00189 static QofLogModule log_module = GNC_MOD_ENGINE;
00190 
00191 enum
00192 {
00193     PROP_0,
00194     PROP_NUM,
00195     PROP_DESCRIPTION,
00196     PROP_CURRENCY,
00197     PROP_POST_DATE,
00198     PROP_ENTER_DATE
00199 };
00200 
00201 void check_open (const Transaction *trans)
00202 {
00203     if (trans && 0 >= qof_instance_get_editlevel(trans))
00204         PERR ("transaction %p not open for editing", trans);
00205 }
00206 /********************************************************************\
00207 \********************************************************************/
00208 gboolean
00209 xaccTransStillHasSplit(const Transaction *trans, const Split *s)
00210 {
00211     return (s->parent == trans && !qof_instance_get_destroying(s));
00212 }
00213 
00214 /* Executes 'cmd_block' for each split currently in the transaction,
00215  * using the in-edit state.  Use the variable 's' for each split. */
00216 #define FOR_EACH_SPLIT(trans, cmd_block) do {                           \
00217         GList *splits;                                                  \
00218         for (splits = (trans)->splits; splits; splits = splits->next) { \
00219             Split *s = splits->data;                                    \
00220             if (xaccTransStillHasSplit(trans, s)) {                     \
00221                 cmd_block;                                              \
00222             }                                                           \
00223         }                                                               \
00224     } while (0)
00225 
00226 G_INLINE_FUNC void mark_trans (Transaction *trans);
00227 void mark_trans (Transaction *trans)
00228 {
00229     FOR_EACH_SPLIT(trans, mark_split(s));
00230 }
00231 
00232 G_INLINE_FUNC void gen_event_trans (Transaction *trans);
00233 void gen_event_trans (Transaction *trans)
00234 {
00235 #ifndef REGISTER_STILL_DEPENDS_ON_ACCOUNT_EVENTS
00236     GList *node;
00237 
00238     for (node = trans->splits; node; node = node->next)
00239     {
00240         Split *s = node->data;
00241         Account *account = s->acc;
00242         GNCLot *lot = s->lot;
00243         if (account)
00244             qof_event_gen (&account->inst, GNC_EVENT_ITEM_CHANGED, s);
00245 
00246         if (lot)
00247         {
00248             /* A change of transaction date might affect opening date of lot */
00249             qof_event_gen (QOF_INSTANCE(lot), QOF_EVENT_MODIFY, NULL);
00250         }
00251     }
00252 #endif
00253 }
00254 
00255 /* GObject Initialization */
00256 G_DEFINE_TYPE(Transaction, gnc_transaction, QOF_TYPE_INSTANCE)
00257 
00258 static void
00259 gnc_transaction_init(Transaction* trans)
00260 {
00261     ENTER ("trans=%p", trans);
00262     /* Fill in some sane defaults */
00263     trans->num         = CACHE_INSERT("");
00264     trans->description = CACHE_INSERT("");
00265 
00266     trans->common_currency = NULL;
00267     trans->splits = NULL;
00268 
00269     trans->date_entered.tv_sec  = 0;
00270     trans->date_entered.tv_nsec = 0;
00271 
00272     trans->date_posted.tv_sec  = 0;
00273     trans->date_posted.tv_nsec = 0;
00274 
00275     trans->marker = 0;
00276     trans->orig = NULL;
00277     LEAVE (" ");
00278 }
00279 
00280 static void
00281 gnc_transaction_dispose(GObject *txnp)
00282 {
00283     G_OBJECT_CLASS(gnc_transaction_parent_class)->dispose(txnp);
00284 }
00285 
00286 static void
00287 gnc_transaction_finalize(GObject* txnp)
00288 {
00289     G_OBJECT_CLASS(gnc_transaction_parent_class)->finalize(txnp);
00290 }
00291 
00292 static void
00293 gnc_transaction_get_property(GObject* object,
00294                              guint prop_id,
00295                              GValue* value,
00296                              GParamSpec* pspec)
00297 {
00298     Transaction* tx;
00299 
00300     g_return_if_fail(GNC_IS_TRANSACTION(object));
00301 
00302     tx = GNC_TRANSACTION(object);
00303     switch (prop_id)
00304     {
00305     case PROP_NUM:
00306         g_value_set_string(value, tx->num);
00307         break;
00308     case PROP_DESCRIPTION:
00309         g_value_set_string(value, tx->description);
00310         break;
00311     case PROP_CURRENCY:
00312         g_value_set_object(value, tx->common_currency);
00313         break;
00314     case PROP_POST_DATE:
00315         g_value_set_boxed(value, &tx->date_posted);
00316         break;
00317     case PROP_ENTER_DATE:
00318         g_value_set_boxed(value, &tx->date_entered);
00319         break;
00320     default:
00321         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
00322         break;
00323     }
00324 }
00325 
00326 static void
00327 gnc_transaction_set_property(GObject* object,
00328                              guint prop_id,
00329                              const GValue* value,
00330                              GParamSpec* pspec)
00331 {
00332     Transaction* tx;
00333 
00334     g_return_if_fail(GNC_IS_TRANSACTION(object));
00335 
00336     tx = GNC_TRANSACTION(object);
00337     switch (prop_id)
00338     {
00339     case PROP_NUM:
00340         xaccTransSetNum( tx, g_value_get_string(value));
00341         break;
00342     case PROP_DESCRIPTION:
00343         xaccTransSetDescription(tx, g_value_get_string(value));
00344         break;
00345     case PROP_CURRENCY:
00346         xaccTransSetCurrency(tx, g_value_get_object(value));
00347         break;
00348     case PROP_POST_DATE:
00349         xaccTransSetDatePostedTS(tx, g_value_get_boxed(value));
00350         break;
00351     case PROP_ENTER_DATE:
00352         xaccTransSetDateEnteredTS(tx, g_value_get_boxed(value));
00353         break;
00354     default:
00355         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
00356         break;
00357     }
00358 }
00359 
00360 static void
00361 gnc_transaction_class_init(TransactionClass* klass)
00362 {
00363     GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
00364 
00365     gobject_class->dispose = gnc_transaction_dispose;
00366     gobject_class->finalize = gnc_transaction_finalize;
00367     gobject_class->set_property = gnc_transaction_set_property;
00368     gobject_class->get_property = gnc_transaction_get_property;
00369 
00370     g_object_class_install_property
00371     (gobject_class,
00372      PROP_NUM,
00373      g_param_spec_string("num",
00374                          "Transaction Number",
00375                          "The transactionNumber is an arbitrary string "
00376                          "assigned by the user.  It is intended to be "
00377                          "a short 1-6 character string that is displayed "
00378                          "by the register.  For checks, it is usually the "
00379                          "check number.  For other types of transactions, "
00380                          "it can be any string.",
00381                          NULL,
00382                          G_PARAM_READWRITE));
00383 
00384     g_object_class_install_property
00385     (gobject_class,
00386      PROP_DESCRIPTION,
00387      g_param_spec_string("description",
00388                          "Transaction Description",
00389                          "The transaction description is an arbitrary string "
00390                          "assigned by the user.  It is usually the customer, "
00391                          "vendor or other organization associated with the "
00392                          "transaction.",
00393                          NULL,
00394                          G_PARAM_READWRITE));
00395 
00396     g_object_class_install_property
00397     (gobject_class,
00398      PROP_CURRENCY,
00399      g_param_spec_object ("currency",
00400                           "Currency",
00401                           "The base currency for this transaction.",
00402                           GNC_TYPE_COMMODITY,
00403                           G_PARAM_READWRITE));
00404 
00405     g_object_class_install_property
00406     (gobject_class,
00407      PROP_POST_DATE,
00408      g_param_spec_boxed("post-date",
00409                         "Post Date",
00410                         "The date the transaction occurred.",
00411                         GNC_TYPE_NUMERIC,
00412                         G_PARAM_READWRITE));
00413 
00414     g_object_class_install_property
00415     (gobject_class,
00416      PROP_ENTER_DATE,
00417      g_param_spec_boxed("enter-date",
00418                         "Enter Date",
00419                         "The date the transaction was entered.",
00420                         GNC_TYPE_NUMERIC,
00421                         G_PARAM_READWRITE));
00422 }
00423 
00424 /********************************************************************\
00425  * xaccInitTransaction
00426  * Initialize a transaction structure
00427 \********************************************************************/
00428 
00429 static void
00430 xaccInitTransaction (Transaction * trans, QofBook *book)
00431 {
00432     ENTER ("trans=%p", trans);
00433     qof_instance_init_data (&trans->inst, GNC_ID_TRANS, book);
00434     LEAVE (" ");
00435 }
00436 
00437 /********************************************************************\
00438 \********************************************************************/
00439 
00440 Transaction *
00441 xaccMallocTransaction (QofBook *book)
00442 {
00443     Transaction *trans;
00444 
00445     g_return_val_if_fail (book, NULL);
00446 
00447     trans = g_object_new(GNC_TYPE_TRANSACTION, NULL);
00448     xaccInitTransaction (trans, book);
00449     qof_event_gen (&trans->inst, QOF_EVENT_CREATE, NULL);
00450 
00451     return trans;
00452 }
00453 
00454 
00455 void
00456 xaccTransSortSplits (Transaction *trans)
00457 {
00458     GList *node, *new_list = NULL;
00459     Split *split;
00460 
00461     /* first debits */
00462     for (node = trans->splits; node; node = node->next)
00463     {
00464         split = node->data;
00465         if (gnc_numeric_negative_p (xaccSplitGetValue(split)))
00466             continue;
00467         new_list = g_list_append(new_list, split);
00468     }
00469 
00470     /* then credits */
00471     for (node = trans->splits; node; node = node->next)
00472     {
00473         split = node->data;
00474         if (!gnc_numeric_negative_p (xaccSplitGetValue(split)))
00475             continue;
00476         new_list = g_list_append(new_list, split);
00477     }
00478 
00479     /* install newly sorted list */
00480     g_list_free(trans->splits);
00481     trans->splits = new_list;
00482 }
00483 
00484 
00485 /********************************************************************\
00486 \********************************************************************/
00487 /* This routine is not exposed externally, since it does weird things,
00488  * like not really owning the splits correctly, and other weirdnesses.
00489  * This routine is prone to programmer snafu if not used correctly.
00490  * It is used only by the edit-rollback code.
00491  */
00492 /* Actually, it *is* public, and used by Period.c */
00493 Transaction *
00494 xaccDupeTransaction (const Transaction *from)
00495 {
00496     Transaction *to;
00497     GList *node;
00498 
00499     to = g_object_new (GNC_TYPE_TRANSACTION, NULL);
00500 
00501     to->num         = CACHE_INSERT (from->num);
00502     to->description = CACHE_INSERT (from->description);
00503 
00504     to->splits = g_list_copy (from->splits);
00505     for (node = to->splits; node; node = node->next)
00506     {
00507         node->data = xaccDupeSplit (node->data);
00508     }
00509 
00510     to->date_entered = from->date_entered;
00511     to->date_posted = from->date_posted;
00512     qof_instance_copy_version(to, from);
00513     to->orig = NULL;
00514 
00515     to->common_currency = from->common_currency;
00516 
00517     /* Trash the guid and entity table. We don't want to mistake
00518      * the cloned transaction as something official.  If we ever
00519      * use this transaction, we'll have to fix this up.
00520      */
00521     to->inst.e_type = NULL;
00522     qof_instance_set_guid(to, guid_null());
00523     qof_instance_copy_book(to, from);
00524     to->inst.kvp_data = kvp_frame_copy (from->inst.kvp_data);
00525 
00526     return to;
00527 }
00528 
00529 /*
00530  * Use this routine to externally duplicate a transaction.  It creates
00531  * a full fledged transaction with unique guid, splits, etc.
00532  */
00533 Transaction *
00534 xaccTransClone (const Transaction *from)
00535 {
00536     Transaction *to;
00537     Split *split;
00538     GList *node;
00539 
00540     qof_event_suspend();
00541     to = g_object_new (GNC_TYPE_TRANSACTION, NULL);
00542 
00543     to->date_entered    = from->date_entered;
00544     to->date_posted     = from->date_posted;
00545     to->num             = CACHE_INSERT (from->num);
00546     to->description     = CACHE_INSERT (from->description);
00547     to->common_currency = from->common_currency;
00548     qof_instance_copy_version(to, from);
00549     qof_instance_copy_version_check(to, from);
00550 
00551     to->orig            = NULL;
00552 
00553     qof_instance_init_data (&to->inst, GNC_ID_TRANS, qof_instance_get_book(from));
00554     kvp_frame_delete (to->inst.kvp_data);
00555     to->inst.kvp_data    = kvp_frame_copy (from->inst.kvp_data);
00556 
00557     xaccTransBeginEdit(to);
00558     for (node = from->splits; node; node = node->next)
00559     {
00560         split = xaccSplitClone(node->data);
00561         split->parent = to;
00562         to->splits = g_list_append (to->splits, split);
00563     }
00564     qof_instance_set_dirty(QOF_INSTANCE(to));
00565     xaccTransCommitEdit(to);
00566     qof_event_resume();
00567 
00568     return to;
00569 }
00570 
00571 
00572 /********************************************************************\
00573 \********************************************************************/
00574 
00575 static void
00576 xaccFreeTransaction (Transaction *trans)
00577 {
00578     GList *node;
00579 
00580     if (!trans) return;
00581 
00582     ENTER ("(addr=%p)", trans);
00583     if (((char *) 1) == trans->num)
00584     {
00585         PERR ("double-free %p", trans);
00586         LEAVE (" ");
00587         return;
00588     }
00589 
00590     /* free up the destination splits */
00591     for (node = trans->splits; node; node = node->next)
00592         xaccFreeSplit (node->data);
00593     g_list_free (trans->splits);
00594     trans->splits = NULL;
00595 
00596     /* free up transaction strings */
00597     CACHE_REMOVE(trans->num);
00598     CACHE_REMOVE(trans->description);
00599 
00600     /* Just in case someone looks up freed memory ... */
00601     trans->num         = (char *) 1;
00602     trans->description = NULL;
00603 
00604     trans->date_entered.tv_sec = 0;
00605     trans->date_entered.tv_nsec = 0;
00606     trans->date_posted.tv_sec = 0;
00607     trans->date_posted.tv_nsec = 0;
00608 
00609     if (trans->orig)
00610     {
00611         xaccFreeTransaction (trans->orig);
00612         trans->orig = NULL;
00613     }
00614 
00615     /* qof_instance_release (&trans->inst); */
00616     g_object_unref(trans);
00617 
00618     LEAVE ("(addr=%p)", trans);
00619 }
00620 
00621 /********************************************************************
00622  xaccTransEqual
00623 
00624  Compare two transactions for equality.  We don't pay any attention to
00625  rollback issues here, and we only care about equality of "permanent
00626  fields", basically the things that would survive a file save/load
00627  cycle.
00628 
00629  ********************************************************************/
00630 
00631 /* return 0 when splits have equal guids */
00632 static gint
00633 compare_split_guids (gconstpointer a, gconstpointer b)
00634 {
00635     const Split *sa = a;
00636     const Split *sb = b;
00637 
00638     if (sa == sb) return 0;
00639     if (!sa || !sb) return 1;
00640 
00641     return guid_compare (xaccSplitGetGUID (sa), xaccSplitGetGUID (sb));
00642 }
00643 
00644 gboolean
00645 xaccTransEqual(const Transaction *ta, const Transaction *tb,
00646                gboolean check_guids,
00647                gboolean check_splits,
00648                gboolean check_balances,
00649                gboolean assume_ordered)
00650 {
00651     gboolean same_book;
00652 
00653     if (!ta && !tb) return TRUE; /* Arguable.  FALSE may be better. */
00654 
00655     if (!ta || !tb)
00656     {
00657         PWARN ("one is NULL");
00658         return FALSE;
00659     }
00660 
00661     if (ta == tb) return TRUE;
00662 
00663     same_book = qof_instance_get_book(QOF_INSTANCE(ta)) == qof_instance_get_book(QOF_INSTANCE(tb));
00664 
00665     if (check_guids)
00666     {
00667         if (qof_instance_guid_compare(ta, tb) != 0)
00668         {
00669             PWARN ("GUIDs differ");
00670             return FALSE;
00671         }
00672     }
00673 
00674     if (!gnc_commodity_equal(ta->common_currency, tb->common_currency))
00675     {
00676         PWARN ("commodities differ %s vs %s",
00677                gnc_commodity_get_unique_name (ta->common_currency),
00678                gnc_commodity_get_unique_name (tb->common_currency));
00679         return FALSE;
00680     }
00681 
00682     if (timespec_cmp(&(ta->date_entered), &(tb->date_entered)))
00683     {
00684         char buf1[100];
00685         char buf2[100];
00686 
00687         (void)gnc_timespec_to_iso8601_buff(ta->date_entered, buf1);
00688         (void)gnc_timespec_to_iso8601_buff(tb->date_entered, buf2);
00689         PWARN ("date entered differs: '%s' vs '%s'", buf1, buf2);
00690         return FALSE;
00691     }
00692 
00693     if (timespec_cmp(&(ta->date_posted), &(tb->date_posted)))
00694     {
00695         char buf1[100];
00696         char buf2[100];
00697 
00698         (void)gnc_timespec_to_iso8601_buff(ta->date_posted, buf1);
00699         (void)gnc_timespec_to_iso8601_buff(tb->date_posted, buf2);
00700         PWARN ("date posted differs: '%s' vs '%s'", buf1, buf2);
00701         return FALSE;
00702     }
00703 
00704     /* If the same book, since we use cached strings, we can just compare pointer
00705      * equality for num and description
00706      */
00707     if ((same_book && ta->num != tb->num) || (!same_book && safe_strcmp(ta->num, tb->num) != 0))
00708     {
00709         PWARN ("num differs: %s vs %s", ta->num, tb->num);
00710         return FALSE;
00711     }
00712 
00713     if ((same_book && ta->description != tb->description)
00714             || (!same_book && safe_strcmp(ta->description, tb->description)))
00715     {
00716         PWARN ("descriptions differ: %s vs %s", ta->description, tb->description);
00717         return FALSE;
00718     }
00719 
00720     if (kvp_frame_compare(ta->inst.kvp_data, tb->inst.kvp_data) != 0)
00721     {
00722         char *frame_a;
00723         char *frame_b;
00724 
00725         frame_a = kvp_frame_to_string (ta->inst.kvp_data);
00726         frame_b = kvp_frame_to_string (tb->inst.kvp_data);
00727 
00728         PWARN ("kvp frames differ:\n%s\n\nvs\n\n%s", frame_a, frame_b);
00729 
00730         g_free (frame_a);
00731         g_free (frame_b);
00732 
00733         return FALSE;
00734     }
00735 
00736     if (check_splits)
00737     {
00738         if ((!ta->splits && tb->splits) || (!tb->splits && ta->splits))
00739         {
00740             PWARN ("only one has splits");
00741             return FALSE;
00742         }
00743 
00744         if (ta->splits && tb->splits)
00745         {
00746             GList *node_a, *node_b;
00747 
00748             for (node_a = ta->splits, node_b = tb->splits;
00749                     node_a;
00750                     node_a = node_a->next, node_b = node_b->next)
00751             {
00752                 Split *split_a = node_a->data;
00753                 Split *split_b;
00754 
00755                 /* don't presume that the splits are in the same order */
00756                 if (!assume_ordered)
00757                     node_b = g_list_find_custom (tb->splits, split_a,
00758                                                  compare_split_guids);
00759 
00760                 if (!node_b)
00761                 {
00762                     PWARN ("first has split %s and second does not",
00763                            guid_to_string (xaccSplitGetGUID (split_a)));
00764                     return FALSE;
00765                 }
00766 
00767                 split_b = node_b->data;
00768 
00769                 if (!xaccSplitEqual (split_a, split_b, check_guids, check_balances,
00770                                      FALSE))
00771                 {
00772                     char str_a[GUID_ENCODING_LENGTH + 1];
00773                     char str_b[GUID_ENCODING_LENGTH + 1];
00774 
00775                     guid_to_string_buff (xaccSplitGetGUID (split_a), str_a);
00776                     guid_to_string_buff (xaccSplitGetGUID (split_b), str_b);
00777 
00778                     PWARN ("splits %s and %s differ", str_a, str_b);
00779                     return FALSE;
00780                 }
00781             }
00782 
00783             if (g_list_length (ta->splits) != g_list_length (tb->splits))
00784             {
00785                 PWARN ("different number of splits");
00786                 return FALSE;
00787             }
00788         }
00789     }
00790 
00791     return TRUE;
00792 }
00793 
00794 /********************************************************************\
00795 xaccTransUseTradingAccounts
00796 
00797 Returns true if the transaction should include trading account splits if
00798 it involves more than one commodity.
00799 \********************************************************************/
00800 
00801 gboolean xaccTransUseTradingAccounts(const Transaction *trans)
00802 {
00803     return qof_book_use_trading_accounts(qof_instance_get_book (trans));
00804 }
00805 
00806 /********************************************************************\
00807 \********************************************************************/
00808 
00809 Transaction *
00810 xaccTransLookup (const GncGUID *guid, QofBook *book)
00811 {
00812     QofCollection *col;
00813     if (!guid || !book) return NULL;
00814     col = qof_book_get_collection (book, GNC_ID_TRANS);
00815     return (Transaction *) qof_collection_lookup_entity (col, guid);
00816 }
00817 
00818 /********************************************************************\
00819 \********************************************************************/
00820 
00821 gnc_numeric
00822 xaccTransGetImbalanceValue (const Transaction * trans)
00823 {
00824     gnc_numeric imbal = gnc_numeric_zero();
00825     if (!trans) return imbal;
00826 
00827     ENTER("(trans=%p)", trans);
00828     /* Could use xaccSplitsComputeValue, except that we want to use
00829        GNC_HOW_DENOM_EXACT */
00830     FOR_EACH_SPLIT(trans, imbal =
00831                        gnc_numeric_add(imbal, xaccSplitGetValue(s),
00832                                        GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT));
00833     LEAVE("(trans=%p) imbal=%s", trans, gnc_num_dbg_to_string(imbal));
00834     return imbal;
00835 }
00836 
00837 MonetaryList *
00838 xaccTransGetImbalance (const Transaction * trans)
00839 {
00840     /* imbal_value is used if either (1) the transaction has a non currency
00841        split or (2) all the splits are in the same currency.  If there are
00842        no non-currency splits and not all splits are in the same currency then
00843        imbal_list is used to compute the imbalance. */
00844     MonetaryList *imbal_list = NULL;
00845     gnc_numeric imbal_value = gnc_numeric_zero();
00846     gboolean trading_accts;
00847 
00848     if (!trans) return imbal_list;
00849 
00850     ENTER("(trans=%p)", trans);
00851 
00852     trading_accts = xaccTransUseTradingAccounts (trans);
00853 
00854     /* If using trading accounts and there is at least one split that is not
00855        in the transaction currency or a split that has a price or exchange
00856        rate other than 1, then compute the balance in each commodity in the
00857        transaction.  Otherwise (all splits are in the transaction's currency)
00858        then compute the balance using the value fields.
00859 
00860        Optimize for the common case of only one currency and a balanced
00861        transaction. */
00862     FOR_EACH_SPLIT(trans,
00863     {
00864         gnc_commodity *commodity;
00865         commodity = xaccAccountGetCommodity(xaccSplitGetAccount(s));
00866         if (trading_accts &&
00867         (imbal_list ||
00868         ! gnc_commodity_equiv(commodity, trans->common_currency) ||
00869         ! gnc_numeric_equal(xaccSplitGetAmount(s), xaccSplitGetValue(s))))
00870         {
00871             /* Need to use (or already are using) a list of imbalances in each of
00872                the currencies used in the transaction. */
00873             if (! imbal_list)
00874             {
00875                 /* All previous splits have been in the transaction's common
00876                    currency, so imbal_value is in this currency. */
00877                 imbal_list = gnc_monetary_list_add_value(imbal_list,
00878                 trans->common_currency,
00879                 imbal_value);
00880             }
00881             imbal_list = gnc_monetary_list_add_value(imbal_list, commodity,
00882                          xaccSplitGetAmount(s));
00883         }
00884 
00885         /* Add it to the value accumulator in case we need it. */
00886         imbal_value = gnc_numeric_add(imbal_value, xaccSplitGetValue(s),
00887                                       GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
00888     } );
00889 
00890 
00891     if (!imbal_list && !gnc_numeric_zero_p(imbal_value))
00892     {
00893         /* Not balanced and no list, create one.  If we found multiple currencies
00894            and no non-currency commodity then imbal_list will already exist and
00895            we won't get here. */
00896         imbal_list = gnc_monetary_list_add_value(imbal_list,
00897                      trans->common_currency,
00898                      imbal_value);
00899     }
00900 
00901     /* Delete all the zero entries from the list, perhaps leaving an
00902        empty list */
00903     imbal_list = gnc_monetary_list_delete_zeros(imbal_list);
00904 
00905     LEAVE("(trans=%p), imbal=%p", trans, imbal_list);
00906     return imbal_list;
00907 }
00908 
00909 gboolean
00910 xaccTransIsBalanced (const Transaction *trans)
00911 {
00912     MonetaryList *imbal_list;
00913     gboolean result;
00914     if (! gnc_numeric_zero_p(xaccTransGetImbalanceValue(trans)))
00915         return FALSE;
00916 
00917     if (!xaccTransUseTradingAccounts (trans))
00918         return TRUE;
00919 
00920     imbal_list = xaccTransGetImbalance(trans);
00921     result = imbal_list == NULL;
00922     gnc_monetary_list_free(imbal_list);
00923     return result;
00924 }
00925 
00926 gnc_numeric
00927 xaccTransGetAccountValue (const Transaction *trans,
00928                           const Account *acc)
00929 {
00930     gnc_numeric total = gnc_numeric_zero ();
00931     if (!trans || !acc) return total;
00932 
00933     FOR_EACH_SPLIT(trans, if (acc == xaccSplitGetAccount(s))
00934 {
00935     total = gnc_numeric_add (total, xaccSplitGetValue (s),
00936                              GNC_DENOM_AUTO,
00937                              GNC_HOW_DENOM_EXACT);
00938     });
00939     return total;
00940 }
00941 
00942 gnc_numeric
00943 xaccTransGetAccountAmount (const Transaction *trans, const Account *acc)
00944 {
00945     gnc_numeric total = gnc_numeric_zero ();
00946     if (!trans || !acc) return total;
00947 
00948     total = gnc_numeric_convert (total, xaccAccountGetCommoditySCU (acc),
00949                                  GNC_HOW_RND_ROUND_HALF_UP);
00950     FOR_EACH_SPLIT(trans, if (acc == xaccSplitGetAccount(s))
00951                    total = gnc_numeric_add_fixed(
00952                                total, xaccSplitGetAmount(s)));
00953     return total;
00954 }
00955 
00956 gnc_numeric
00957 xaccTransGetAccountConvRate(const Transaction *txn, const Account *acc)
00958 {
00959     gnc_numeric amount, value, convrate;
00960     GList *splits;
00961     Split *s;
00962     gboolean found_acc_match = FALSE;
00963     gnc_commodity *acc_commod = xaccAccountGetCommodity(acc);
00964 
00965     /* We need to compute the conversion rate into _this account_.  So,
00966      * find the first split into this account, compute the conversion
00967      * rate (based on amount/value), and then return this conversion
00968      * rate.
00969      */
00970     if (gnc_commodity_equal(acc_commod, xaccTransGetCurrency(txn)))
00971         return gnc_numeric_create(1, 1);
00972 
00973     for (splits = txn->splits; splits; splits = splits->next)
00974     {
00975         Account *split_acc;
00976         gnc_commodity *split_commod;
00977 
00978         s = splits->data;
00979 
00980         if (!xaccTransStillHasSplit(txn, s))
00981             continue;
00982         split_acc = xaccSplitGetAccount (s);
00983         split_commod = xaccAccountGetCommodity (split_acc);
00984         if (! (split_acc == acc ||
00985                 gnc_commodity_equal (split_commod, acc_commod)))
00986             continue;
00987 
00988         found_acc_match = TRUE;
00989         amount = xaccSplitGetAmount (s);
00990 
00991         /* Ignore splits with "zero" amount */
00992         if (gnc_numeric_zero_p (amount))
00993             continue;
00994 
00995         value = xaccSplitGetValue (s);
00996         if (gnc_numeric_zero_p (value))
00997             PWARN("How can amount be nonzero and value be zero?");
00998 
00999         convrate = gnc_numeric_div(amount, value, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE);
01000         return convrate;
01001     }
01002 
01003     if (acc)
01004     {
01005         /* If we did find a matching account but its amount was zero,
01006          * then perhaps this is a "special" income/loss transaction
01007          */
01008         if (found_acc_match)
01009             return gnc_numeric_zero();
01010         else
01011             PERR("Cannot convert transaction -- no splits with proper conversion ratio");
01012     }
01013     return gnc_numeric_create (100, 100);
01014 }
01015 
01016 gnc_numeric
01017 xaccTransGetAccountBalance (const Transaction *trans,
01018                             const Account *account)
01019 {
01020     GList *node;
01021     Split *last_split = NULL;
01022 
01023     // Not really the appropriate error value.
01024     g_return_val_if_fail(account && trans, gnc_numeric_error(GNC_ERROR_ARG));
01025 
01026     for (node = trans->splits; node; node = node->next)
01027     {
01028         Split *split = node->data;
01029 
01030         if (!xaccTransStillHasSplit(trans, split))
01031             continue;
01032         if (xaccSplitGetAccount(split) != account)
01033             continue;
01034 
01035         if (!last_split)
01036         {
01037             last_split = split;
01038             continue;
01039         }
01040 
01041         /* This test needs to correspond to the comparison function used when
01042            sorting the splits for computing the running balance. */
01043         if (xaccSplitOrder (last_split, split) < 0)
01044             last_split = split;
01045     }
01046 
01047     return xaccSplitGetBalance (last_split);
01048 }
01049 
01050 /********************************************************************\
01051 \********************************************************************/
01052 /* The new routine for setting the common currency */
01053 
01054 gnc_commodity *
01055 xaccTransGetCurrency (const Transaction *trans)
01056 {
01057     return trans ? trans->common_currency : NULL;
01058 }
01059 
01060 void
01061 xaccTransSetCurrency (Transaction *trans, gnc_commodity *curr)
01062 {
01063     gint fraction, old_fraction;
01064 
01065     if (!trans || !curr || trans->common_currency == curr) return;
01066     xaccTransBeginEdit(trans);
01067 
01068     old_fraction = gnc_commodity_get_fraction (trans->common_currency);
01069     trans->common_currency = curr;
01070     fraction = gnc_commodity_get_fraction (curr);
01071 
01072     /* avoid needless crud if fraction didn't change */
01073     if (fraction != old_fraction)
01074     {
01075         FOR_EACH_SPLIT(trans, xaccSplitSetValue(s, xaccSplitGetValue(s)));
01076     }
01077 
01078     qof_instance_set_dirty(QOF_INSTANCE(trans));
01079     mark_trans(trans);  /* Dirty balance of every account in trans */
01080     xaccTransCommitEdit(trans);
01081 }
01082 
01083 /********************************************************************\
01084 \********************************************************************/
01085 
01086 void
01087 xaccTransBeginEdit (Transaction *trans)
01088 {
01089     if (!trans) return;
01090     if (!qof_begin_edit(&trans->inst)) return;
01091 
01092     if (qof_book_shutting_down(qof_instance_get_book(trans))) return;
01093 
01094     if (!qof_book_is_readonly(qof_instance_get_book(trans)))
01095     {
01096         xaccOpenLog ();
01097         xaccTransWriteLog (trans, 'B');
01098     }
01099 
01100     /* Make a clone of the transaction; we will use this
01101      * in case we need to roll-back the edit. */
01102     trans->orig = xaccDupeTransaction (trans);
01103 }
01104 
01105 /********************************************************************\
01106 \********************************************************************/
01107 
01108 void
01109 xaccTransDestroy (Transaction *trans)
01110 {
01111     if (!trans) return;
01112 
01113     if (!xaccTransGetReadOnly (trans) ||
01114             qof_book_shutting_down(qof_instance_get_book(trans)))
01115     {
01116         xaccTransBeginEdit(trans);
01117         qof_instance_set_destroying(trans, TRUE);
01118         xaccTransCommitEdit(trans);
01119     }
01120 }
01121 
01122 static void
01123 destroy_gains (Transaction *trans)
01124 {
01125     SplitList *node;
01126     for (node = trans->splits; node; node = node->next)
01127     {
01128         Split *s = node->data;
01129         if (!xaccTransStillHasSplit(trans, s))
01130             continue;
01131 
01132         if (GAINS_STATUS_UNKNOWN == s->gains) xaccSplitDetermineGainStatus(s);
01133         if (s->gains_split && (GAINS_STATUS_GAINS & s->gains_split->gains))
01134         {
01135             Transaction *t = s->gains_split->parent;
01136             xaccTransDestroy (t);
01137             s->gains_split = NULL;
01138         }
01139     }
01140 }
01141 
01142 static void
01143 do_destroy (Transaction *trans)
01144 {
01145     SplitList *node;
01146     gboolean shutting_down = qof_book_shutting_down(qof_instance_get_book(trans));
01147 
01148     /* If there are capital-gains transactions associated with this,
01149      * they need to be destroyed too unless we're shutting down in
01150      * which case all transactions will be destroyed. */
01151     if (!shutting_down)
01152         destroy_gains (trans);
01153 
01154     /* Make a log in the journal before destruction.  */
01155     if (!shutting_down && !qof_book_is_readonly(qof_instance_get_book(trans)))
01156         xaccTransWriteLog (trans, 'D');
01157 
01158     qof_event_gen (&trans->inst, QOF_EVENT_DESTROY, NULL);
01159 
01160     /* We only own the splits that still think they belong to us.   This is done
01161        in 2 steps.  In the first, the splits are marked as being destroyed, but they
01162        are not destroyed yet.  In the second, the destruction is committed which will
01163        do the actual destruction.  If both steps are done for a split before they are
01164        done for the next split, then a split will still be on the split list after it
01165        has been freed.  This can cause other parts of the code (e.g. in xaccSplitDestroy())
01166        to reference the split after it has been freed. */
01167     for (node = trans->splits; node; node = node->next)
01168     {
01169         Split *s = node->data;
01170         if (s->parent == trans)
01171         {
01172             xaccSplitDestroy(s);
01173         }
01174     }
01175     for (node = trans->splits; node; node = node->next)
01176     {
01177         Split *s = node->data;
01178         if (s->parent == trans)
01179         {
01180             xaccSplitCommitEdit(s);
01181         }
01182     }
01183     g_list_free (trans->splits);
01184     trans->splits = NULL;
01185     xaccFreeTransaction (trans);
01186 }
01187 
01188 /********************************************************************\
01189 \********************************************************************/
01190 
01191 /* Temporary hack for data consistency */
01192 static int scrub_data = 1;
01193 void xaccEnableDataScrubbing(void)
01194 {
01195     scrub_data = 1;
01196 }
01197 void xaccDisableDataScrubbing(void)
01198 {
01199     scrub_data = 0;
01200 }
01201 
01202 /* Check for an implicitly deleted transaction */
01203 static gboolean was_trans_emptied(Transaction *trans)
01204 {
01205     FOR_EACH_SPLIT(trans, return FALSE);
01206     return TRUE;
01207 }
01208 
01209 static void trans_on_error(Transaction *trans, QofBackendError errcode)
01210 {
01211     /* If the backend puked, then we must roll-back
01212      * at this point, and let the user know that we failed.
01213      * The GUI should check for error conditions ...
01214      */
01215     if (ERR_BACKEND_MODIFIED == errcode)
01216     {
01217         PWARN("Another user has modified this transaction\n"
01218               "\tjust a moment ago. Please look at their changes,\n"
01219               "\tand try again, if needed.\n");
01220     }
01221 
01222     xaccTransRollbackEdit(trans);
01223     gnc_engine_signal_commit_error( errcode );
01224 }
01225 
01226 static void trans_cleanup_commit(Transaction *trans)
01227 {
01228     GList *slist, *node;
01229 
01230     /* ------------------------------------------------- */
01231     /* Make sure all associated splits are in proper order
01232      * in their accounts with the correct balances. */
01233 
01234     /* Iterate over existing splits */
01235     slist = g_list_copy(trans->splits);
01236     for (node = slist; node; node = node->next)
01237     {
01238         Split *s = node->data;
01239         if (!qof_instance_is_dirty(QOF_INSTANCE(s)))
01240             continue;
01241 
01242         if ((s->parent != trans) || qof_instance_get_destroying(s))
01243         {
01244             /* Existing split either moved to another transaction or
01245                was destroyed, drop from list */
01246             GncEventData ed;
01247             ed.node = trans;
01248             ed.idx = g_list_index(trans->splits, s);
01249             trans->splits = g_list_remove(trans->splits, s);
01250             qof_event_gen(&s->inst, QOF_EVENT_REMOVE, &ed);
01251         }
01252 
01253         if (s->parent == trans)
01254         {
01255             /* Split was either added, destroyed or just changed */
01256             if (qof_instance_get_destroying(s))
01257                 qof_event_gen(&s->inst, QOF_EVENT_DESTROY, NULL);
01258             else qof_event_gen(&s->inst, QOF_EVENT_MODIFY, NULL);
01259             xaccSplitCommitEdit(s);
01260         }
01261     }
01262     g_list_free(slist);
01263 
01264     if (!qof_book_is_readonly(qof_instance_get_book(trans)))
01265         xaccTransWriteLog (trans, 'C');
01266 
01267     /* Get rid of the copy we made. We won't be rolling back,
01268      * so we don't need it any more.  */
01269     PINFO ("get rid of rollback trans=%p", trans->orig);
01270     xaccFreeTransaction (trans->orig);
01271     trans->orig = NULL;
01272 
01273     /* Sort the splits. Why do we need to do this ?? */
01274     /* Good question.  Who knows?  */
01275     xaccTransSortSplits(trans);
01276 
01277     /* Put back to zero. */
01278     qof_instance_decrease_editlevel(trans);
01279     g_assert(qof_instance_get_editlevel(trans) == 0);
01280 
01281     gen_event_trans (trans); //TODO: could be conditional
01282     qof_event_gen (&trans->inst, QOF_EVENT_MODIFY, NULL);
01283 }
01284 
01285 void
01286 xaccTransCommitEdit (Transaction *trans)
01287 {
01288     if (!trans) return;
01289     ENTER ("(trans=%p)", trans);
01290 
01291     if (!qof_commit_edit (QOF_INSTANCE(trans)))
01292     {
01293         LEAVE("editlevel non-zero");
01294         return;
01295     }
01296 
01297     /* We increment this for the duration of the call
01298      * so other functions don't result in a recursive
01299      * call to xaccTransCommitEdit. */
01300     qof_instance_increase_editlevel(trans);
01301 
01302     if (was_trans_emptied(trans))
01303         qof_instance_set_destroying(trans, TRUE);
01304 
01305     /* Before committing the transaction, we are going to enforce certain
01306      * constraints.  In particular, we want to enforce the cap-gains
01307      * and the balanced lot constraints.  These constraints might
01308      * change the number of splits in this transaction, and the
01309      * transaction itself might be deleted.  This is also why
01310      * we can't really enforce these constraints elsewhere: they
01311      * can cause pointers to splits and transactions to disappear out
01312      * from under the holder.
01313      */
01314     if (!qof_instance_get_destroying(trans) && scrub_data &&
01315             !qof_book_shutting_down(xaccTransGetBook(trans)))
01316     {
01317         /* If scrubbing gains recurses through here, don't call it again. */
01318         scrub_data = 0;
01319         /* The total value of the transaction should sum to zero.
01320          * Call the trans scrub routine to fix it. Indirectly, this
01321          * routine also performs a number of other transaction fixes too.
01322          */
01323         xaccTransScrubImbalance (trans, NULL, NULL);
01324         /* Get the cap gains into a consistent state as well. */
01325 
01326         /* Lot Scrubbing is temporarily disabled. */
01327         if (g_getenv("GNC_AUTO_SCRUB_LOTS") != NULL)
01328             xaccTransScrubGains (trans, NULL);
01329 
01330         /* Allow scrubbing in transaction commit again */
01331         scrub_data = 1;
01332     }
01333 
01334     /* Record the time of last modification */
01335     if (0 == trans->date_entered.tv_sec)
01336     {
01337         struct timeval tv;
01338 #ifdef HAVE_GETTIMEOFDAY
01339         gettimeofday (&tv, NULL);
01340 #else
01341         time (&(tv.tv_sec));
01342         tv.tv_usec = 0;
01343 #endif
01344         trans->date_entered.tv_sec = tv.tv_sec;
01345 //        trans->date_entered.tv_nsec = 1000 * tv.tv_usec;
01346         qof_instance_set_dirty(QOF_INSTANCE(trans));
01347     }
01348 
01349     qof_commit_edit_part2(QOF_INSTANCE(trans),
01350                           (void (*) (QofInstance *, QofBackendError))
01351                           trans_on_error,
01352                           (void (*) (QofInstance *)) trans_cleanup_commit,
01353                           (void (*) (QofInstance *)) do_destroy);
01354     LEAVE ("(trans=%p)", trans);
01355 }
01356 
01357 #define SWAP(a, b) do { gpointer tmp = (a); (a) = (b); (b) = tmp; } while (0);
01358 
01359 /* Ughhh. The Rollback function is terribly complex, and, what's worse,
01360  * it only rolls back the basics.  The TransCommit functions did a bunch
01361  * of Lot/Cap-gains scrubbing that don't get addressed/undone here, and
01362  * so the rollback can potentially leave a bit of a mess behind.  We
01363  * really need a more robust undo capability.  Part of the problem is
01364  * that the biggest user of the undo is the multi-user backend, which
01365  * also adds complexity.
01366  */
01367 void
01368 xaccTransRollbackEdit (Transaction *trans)
01369 {
01370     GList *node, *onode;
01371     QofBackend *be;
01372     Transaction *orig;
01373     GList *slist;
01374     int num_preexist, i;
01375     ENTER ("trans addr=%p\n", trans);
01376 
01377     check_open(trans);
01378 
01379     /* copy the original values back in. */
01380     orig = trans->orig;
01381     SWAP(trans->num, orig->num);
01382     SWAP(trans->description, orig->description);
01383     trans->date_entered = orig->date_entered;
01384     trans->date_posted = orig->date_posted;
01385     SWAP(trans->common_currency, orig->common_currency);
01386     SWAP(trans->inst.kvp_data, orig->inst.kvp_data);
01387 
01388     /* The splits at the front of trans->splits are exactly the same
01389        splits as in the original, but some of them may have changed, so
01390        we restore only those. */
01391     num_preexist = g_list_length(orig->splits);
01392     slist = g_list_copy(trans->splits);
01393     for (i = 0, node = slist, onode = orig->splits; node;
01394             i++, node = node->next, onode = onode ? onode->next : NULL)
01395     {
01396         Split *s = node->data;
01397 
01398         if (!qof_instance_is_dirty(QOF_INSTANCE(s)))
01399             continue;
01400 
01401         if (i < num_preexist)
01402         {
01403             Split *so = onode->data;
01404 
01405             xaccSplitRollbackEdit(s);
01406             SWAP(s->action, so->action);
01407             SWAP(s->memo, so->memo);
01408             SWAP(s->inst.kvp_data, so->inst.kvp_data);
01409             s->reconciled = so->reconciled;
01410             s->amount = so->amount;
01411             s->value = so->value;
01412             s->lot = so->lot;
01413             s->gains_split = so->gains_split;
01414             //SET_GAINS_A_VDIRTY(s);
01415             s->date_reconciled = so->date_reconciled;
01416             qof_instance_mark_clean(QOF_INSTANCE(s));
01417             xaccFreeSplit(so);
01418         }
01419         else
01420         {
01421             /* Potentially added splits */
01422             if (trans != xaccSplitGetParent(s))
01423             {
01424                 trans->splits = g_list_remove(trans->splits, s);
01425                 /* New split added, but then moved to another
01426                    transaction */
01427                 continue;
01428             }
01429             xaccSplitRollbackEdit(s);
01430             trans->splits = g_list_remove(trans->splits, s);
01431             g_assert(trans != xaccSplitGetParent(s));
01432             /* NB: our memory management policy here is that a new split
01433                added to the transaction which is then rolled-back still
01434                belongs to the engine.  Specifically, it's freed by the
01435                transaction to which it was added.  Don't add the Split to
01436                more than one transaction during the begin/commit block! */
01437             if (NULL == xaccSplitGetParent(s))
01438             {
01439                 xaccFreeSplit(s);  // a newly malloc'd split
01440             }
01441         }
01442     }
01443     g_list_free(slist);
01444     g_list_free(orig->splits);
01445     orig->splits = NULL;
01446 
01447     /* Now that the engine copy is back to its original version,
01448      * get the backend to fix it in the database */
01449     be = qof_book_get_backend(qof_instance_get_book(trans));
01452     if (be && be->rollback)
01453     {
01454         QofBackendError errcode;
01455 
01456         /* clear errors */
01457         do
01458         {
01459             errcode = qof_backend_get_error (be);
01460         }
01461         while (ERR_BACKEND_NO_ERR != errcode);
01462 
01463         (be->rollback) (be, &(trans->inst));
01464 
01465         errcode = qof_backend_get_error (be);
01466         if (ERR_BACKEND_MOD_DESTROY == errcode)
01467         {
01468             /* The backend is asking us to delete this transaction.
01469              * This typically happens because another (remote) user
01470              * has deleted this transaction, and we haven't found
01471              * out about it until this user tried to edit it.
01472              */
01473             xaccTransDestroy (trans);
01474             do_destroy (trans);
01475 
01476             /* push error back onto the stack */
01477             qof_backend_set_error (be, errcode);
01478             LEAVE ("deleted trans addr=%p\n", trans);
01479             return;
01480         }
01481         if (ERR_BACKEND_NO_ERR != errcode)
01482         {
01483             PERR ("Rollback Failed.  Ouch!");
01484             /* push error back onto the stack */
01485             qof_backend_set_error (be, errcode);
01486         }
01487     }
01488 
01489     if (!qof_book_is_readonly(qof_instance_get_book(trans)))
01490         xaccTransWriteLog (trans, 'R');
01491 
01492     xaccFreeTransaction (trans->orig);
01493 
01494     trans->orig = NULL;
01495     qof_instance_set_destroying(trans, FALSE);
01496 
01497     /* Put back to zero. */
01498     qof_instance_decrease_editlevel(trans);
01499     /* FIXME: The register code seems to depend on the engine to
01500        generate an event during rollback, even though the state is just
01501        reverting to what it was. */
01502     gen_event_trans (trans);
01503 
01504     LEAVE ("trans addr=%p\n", trans);
01505 }
01506 
01507 gboolean
01508 xaccTransIsOpen (const Transaction *trans)
01509 {
01510     return trans ? (0 < qof_instance_get_editlevel(trans)) : FALSE;
01511 }
01512 
01513 #define SECS_PER_DAY 86400
01514 
01515 int
01516 xaccTransOrder (const Transaction *ta, const Transaction *tb)
01517 {
01518     char *da, *db;
01519     int na, nb, retval;
01520 
01521     if ( ta && !tb ) return -1;
01522     if ( !ta && tb ) return +1;
01523     if ( !ta && !tb ) return 0;
01524 
01525     /* if dates differ, return */
01526     DATE_CMP(ta, tb, date_posted);
01527 
01528     /* otherwise, sort on number string */
01529     na = atoi(ta->num);
01530     nb = atoi(tb->num);
01531     if (na < nb) return -1;
01532     if (na > nb) return +1;
01533 
01534     /* if dates differ, return */
01535     DATE_CMP(ta, tb, date_entered);
01536 
01537     /* otherwise, sort on description string */
01538     da = ta->description ? ta->description : "";
01539     db = tb->description ? tb->description : "";
01540     retval = g_utf8_collate (da, db);
01541     if (retval)
01542         return retval;
01543 
01544     /* else, sort on guid - keeps sort stable. */
01545     return qof_instance_guid_compare(ta, tb);
01546 }
01547 
01548 /********************************************************************\
01549 \********************************************************************/
01550 
01551 static inline void
01552 xaccTransSetDateInternal(Transaction *trans, Timespec *dadate, Timespec val)
01553 {
01554     xaccTransBeginEdit(trans);
01555 
01556     {
01557         time_t secs = (time_t) val.tv_sec;
01558         gchar *tstr = ctime(&secs);
01559         PINFO ("addr=%p set date to %" G_GUINT64_FORMAT ".%09ld %s",
01560                trans, val.tv_sec, val.tv_nsec, tstr ? tstr : "(null)");
01561     }
01562 
01563     *dadate = val;
01564     qof_instance_set_dirty(QOF_INSTANCE(trans));
01565     mark_trans(trans);
01566     xaccTransCommitEdit(trans);
01567 
01568     /* Because the date has changed, we need to make sure that each of
01569      * the splits is properly ordered in each of their accounts. We
01570      * could do that here, simply by reinserting each split into its
01571      * account. However, in some ways this is bad behaviour, and it
01572      * seems much better/nicer to defer that until the commit phase,
01573      * i.e. until the user has called the xaccTransCommitEdit()
01574      * routine. So, for now, we are done. */
01575 }
01576 
01577 static inline void
01578 set_gains_date_dirty (Transaction *trans)
01579 {
01580     FOR_EACH_SPLIT(trans, s->gains |= GAINS_STATUS_DATE_DIRTY);
01581 }
01582 
01583 void
01584 xaccTransSetDatePostedSecs (Transaction *trans, time_t secs)
01585 {
01586     Timespec ts = {secs, 0};
01587     if (!trans) return;
01588     xaccTransSetDateInternal(trans, &trans->date_posted, ts);
01589     set_gains_date_dirty (trans);
01590 }
01591 
01592 void
01593 xaccTransSetDatePostedGDate (Transaction *trans, GDate date)
01594 {
01595     KvpValue* kvp_value;
01596     KvpFrame* frame;
01597     if (!trans) return;
01598 
01599     /* We additionally save this date into a kvp frame to ensure in
01600      * the future a date which was set as *date* (without time) can
01601      * clearly be distinguished from the Timespec. */
01602     kvp_value = kvp_value_new_gdate(date);
01603     frame = kvp_frame_set_value_nc(trans->inst.kvp_data, TRANS_DATE_POSTED, kvp_value);
01604     if (!frame)
01605     {
01606         kvp_value_delete(kvp_value);
01607     }
01608 
01609     xaccTransSetDateInternal(trans, &trans->date_posted,
01610                              gdate_to_timespec(date));
01611     set_gains_date_dirty (trans);
01612 }
01613 
01614 void
01615 xaccTransSetDateEnteredSecs (Transaction *trans, time_t secs)
01616 {
01617     Timespec ts = {secs, 0};
01618     if (!trans) return;
01619     xaccTransSetDateInternal(trans, &trans->date_entered, ts);
01620 }
01621 
01622 static void
01623 qofTransSetDatePosted (Transaction *trans, Timespec ts)
01624 {
01625     if (!trans) return;
01626     if ((ts.tv_nsec == 0) && (ts.tv_sec == 0)) return;
01627     if (!qof_begin_edit(&trans->inst)) return;
01628     xaccTransSetDateInternal(trans, &trans->date_posted, ts);
01629     set_gains_date_dirty(trans);
01630     qof_commit_edit(&trans->inst);
01631 }
01632 
01633 void
01634 xaccTransSetDatePostedTS (Transaction *trans, const Timespec *ts)
01635 {
01636     if (!trans || !ts) return;
01637     xaccTransSetDateInternal(trans, &trans->date_posted, *ts);
01638     set_gains_date_dirty (trans);
01639 }
01640 
01641 static void
01642 qofTransSetDateEntered (Transaction *trans, Timespec ts)
01643 {
01644     if (!trans) return;
01645     if ((ts.tv_nsec == 0) && (ts.tv_sec == 0)) return;
01646     if (!qof_begin_edit(&trans->inst)) return;
01647     xaccTransSetDateInternal(trans, &trans->date_entered, ts);
01648     qof_commit_edit(&trans->inst);
01649 }
01650 
01651 void
01652 xaccTransSetDateEnteredTS (Transaction *trans, const Timespec *ts)
01653 {
01654     if (!trans || !ts) return;
01655     xaccTransSetDateInternal(trans, &trans->date_entered, *ts);
01656 }
01657 
01658 void
01659 xaccTransSetDate (Transaction *trans, int day, int mon, int year)
01660 {
01661     GDate *date;
01662     if (!trans) return;
01663     date = g_date_new_dmy(day, mon, year);
01664     g_assert(g_date_valid(date));
01665     xaccTransSetDatePostedGDate(trans, *date);
01666     g_date_free(date);
01667 }
01668 
01669 void
01670 xaccTransSetDateDueTS (Transaction *trans, const Timespec *ts)
01671 {
01672     if (!trans || !ts) return;
01673     xaccTransBeginEdit(trans);
01674     kvp_frame_set_timespec (trans->inst.kvp_data, TRANS_DATE_DUE_KVP, *ts);
01675     qof_instance_set_dirty(QOF_INSTANCE(trans));
01676     xaccTransCommitEdit(trans);
01677 }
01678 
01679 void
01680 xaccTransSetTxnType (Transaction *trans, char type)
01681 {
01682     char s[2] = {type, '\0'};
01683     g_return_if_fail(trans);
01684     xaccTransBeginEdit(trans);
01685     kvp_frame_set_str (trans->inst.kvp_data, TRANS_TXN_TYPE_KVP, s);
01686     qof_instance_set_dirty(QOF_INSTANCE(trans));
01687     xaccTransCommitEdit(trans);
01688 }
01689 
01690 void xaccTransClearReadOnly (Transaction *trans)
01691 {
01692     if (trans)
01693     {
01694         xaccTransBeginEdit(trans);
01695         kvp_frame_set_slot_path (trans->inst.kvp_data, NULL,
01696                                  TRANS_READ_ONLY_REASON, NULL);
01697         qof_instance_set_dirty(QOF_INSTANCE(trans));
01698         xaccTransCommitEdit(trans);
01699     }
01700 }
01701 
01702 void
01703 xaccTransSetReadOnly (Transaction *trans, const char *reason)
01704 {
01705     if (trans && reason)
01706     {
01707         xaccTransBeginEdit(trans);
01708         kvp_frame_set_str (trans->inst.kvp_data,
01709                            TRANS_READ_ONLY_REASON, reason);
01710         qof_instance_set_dirty(QOF_INSTANCE(trans));
01711         xaccTransCommitEdit(trans);
01712     }
01713 }
01714 
01715 /********************************************************************\
01716 \********************************************************************/
01717 
01718 /* QOF does not open the trans before setting a parameter,
01719 but the call uses check_open so we cannot use the call directly. */
01720 static void
01721 qofTransSetNum (Transaction *trans, const char *xnum)
01722 {
01723     if (!qof_begin_edit(&trans->inst)) return;
01724     xaccTransSetNum(trans, xnum);
01725     qof_commit_edit(&trans->inst);
01726 }
01727 
01728 void
01729 xaccTransSetNum (Transaction *trans, const char *xnum)
01730 {
01731     if (!trans || !xnum) return;
01732     xaccTransBeginEdit(trans);
01733 
01734     CACHE_REPLACE(trans->num, xnum);
01735     qof_instance_set_dirty(QOF_INSTANCE(trans));
01736     mark_trans(trans);  /* Dirty balance of every account in trans */
01737     xaccTransCommitEdit(trans);
01738 }
01739 
01740 static void
01741 qofTransSetDescription (Transaction *trans, const char *desc)
01742 {
01743     if (!qof_begin_edit(&trans->inst)) return;
01744     xaccTransSetDescription(trans, desc);
01745     qof_commit_edit(&trans->inst);
01746 }
01747 
01748 void
01749 xaccTransSetDescription (Transaction *trans, const char *desc)
01750 {
01751     if (!trans || !desc) return;
01752     xaccTransBeginEdit(trans);
01753 
01754     CACHE_REPLACE(trans->description, desc);
01755     qof_instance_set_dirty(QOF_INSTANCE(trans));
01756     xaccTransCommitEdit(trans);
01757 }
01758 
01759 static void
01760 qofTransSetNotes (Transaction *trans, const char *notes)
01761 {
01762     if (!qof_begin_edit(&trans->inst)) return;
01763     xaccTransSetNotes(trans, notes);
01764     qof_commit_edit(&trans->inst);
01765 }
01766 
01767 void
01768 xaccTransSetNotes (Transaction *trans, const char *notes)
01769 {
01770     if (!trans || !notes) return;
01771     xaccTransBeginEdit(trans);
01772 
01773     kvp_frame_set_str (trans->inst.kvp_data, trans_notes_str, notes);
01774     qof_instance_set_dirty(QOF_INSTANCE(trans));
01775     xaccTransCommitEdit(trans);
01776 }
01777 
01778 void
01779 xaccTransSetIsClosingTxn (Transaction *trans, gboolean is_closing)
01780 {
01781     if (!trans) return;
01782     xaccTransBeginEdit(trans);
01783 
01784     if (is_closing)
01785         kvp_frame_set_gint64 (trans->inst.kvp_data, trans_is_closing_str, 1);
01786     else
01787         kvp_frame_replace_value_nc (trans->inst.kvp_data, trans_is_closing_str, NULL);
01788     qof_instance_set_dirty(QOF_INSTANCE(trans));
01789     xaccTransCommitEdit(trans);
01790 }
01791 
01792 
01793 /********************************************************************\
01794 \********************************************************************/
01795 
01796 Split *
01797 xaccTransGetSplit (const Transaction *trans, int i)
01798 {
01799     int j = 0;
01800     if (!trans || i < 0) return NULL;
01801 
01802     FOR_EACH_SPLIT(trans, { if (i == j) return s; j++; });
01803     return NULL;
01804 }
01805 
01806 int
01807 xaccTransGetSplitIndex(const Transaction *trans, const Split *split)
01808 {
01809     int j = 0;
01810     g_return_val_if_fail(trans && split, -1);
01811 
01812     FOR_EACH_SPLIT(trans, { if (s == split) return j; j++; });
01813     return -1;
01814 }
01815 
01816 SplitList *
01817 xaccTransGetSplitList (const Transaction *trans)
01818 {
01819     return trans ? trans->splits : NULL;
01820 }
01821 
01822 int
01823 xaccTransCountSplits (const Transaction *trans)
01824 {
01825     gint i = 0;
01826     FOR_EACH_SPLIT(trans, i++);
01827     return i;
01828 }
01829 
01830 const char *
01831 xaccTransGetNum (const Transaction *trans)
01832 {
01833     return trans ? trans->num : NULL;
01834 }
01835 
01836 const char *
01837 xaccTransGetDescription (const Transaction *trans)
01838 {
01839     return trans ? trans->description : NULL;
01840 }
01841 
01842 const char *
01843 xaccTransGetNotes (const Transaction *trans)
01844 {
01845     return trans ?
01846            kvp_frame_get_string (trans->inst.kvp_data, trans_notes_str) : NULL;
01847 }
01848 
01849 gboolean
01850 xaccTransGetIsClosingTxn (const Transaction *trans)
01851 {
01852     return trans ?
01853            kvp_frame_get_gint64 (trans->inst.kvp_data, trans_is_closing_str)
01854            : FALSE;
01855 }
01856 
01857 /********************************************************************\
01858 \********************************************************************/
01859 
01860 time_t
01861 xaccTransGetDate (const Transaction *trans)
01862 {
01863     return trans ? trans->date_posted.tv_sec : 0;
01864 }
01865 
01866 void
01867 xaccTransGetDatePostedTS (const Transaction *trans, Timespec *ts)
01868 {
01869     if (trans && ts)
01870         *ts = trans->date_posted;
01871 }
01872 
01873 void
01874 xaccTransGetDateEnteredTS (const Transaction *trans, Timespec *ts)
01875 {
01876     if (trans && ts)
01877         *ts = trans->date_entered;
01878 }
01879 
01880 Timespec
01881 xaccTransRetDatePostedTS (const Transaction *trans)
01882 {
01883     Timespec ts = {0, 0};
01884     return trans ? trans->date_posted : ts;
01885 }
01886 
01887 GDate
01888 xaccTransGetDatePostedGDate (const Transaction *trans)
01889 {
01890     GDate result;
01891     if (trans)
01892     {
01893         /* Can we look up this value in the kvp slot? If yes, use it
01894          * from there because it doesn't suffer from time zone
01895          * shifts. */
01896         const KvpValue* kvp_value =
01897             kvp_frame_get_slot(trans->inst.kvp_data, TRANS_DATE_POSTED);
01898         if (kvp_value)
01899             result = kvp_value_get_gdate(kvp_value);
01900         else
01901             result = timespec_to_gdate(xaccTransRetDatePostedTS(trans));
01902     }
01903     else
01904     {
01905         g_date_clear(&result, 1);
01906     }
01907     return result;
01908 }
01909 
01910 Timespec
01911 xaccTransRetDateEnteredTS (const Transaction *trans)
01912 {
01913     Timespec ts = {0, 0};
01914     return trans ? trans->date_entered : ts;
01915 }
01916 
01917 void
01918 xaccTransGetDateDueTS (const Transaction *trans, Timespec *ts)
01919 {
01920     KvpValue *value;
01921 
01922     if (!trans || !ts) return;
01923 
01924     value = kvp_frame_get_slot (trans->inst.kvp_data, TRANS_DATE_DUE_KVP);
01925     if (value)
01926         *ts = kvp_value_get_timespec (value);
01927     else
01928         xaccTransGetDatePostedTS (trans, ts);
01929 }
01930 
01931 Timespec
01932 xaccTransRetDateDueTS (const Transaction *trans)
01933 {
01934     Timespec ts = {0, 0};
01935     if (trans) xaccTransGetDateDueTS (trans, &ts);
01936     return ts;
01937 }
01938 
01939 char
01940 xaccTransGetTxnType (const Transaction *trans)
01941 {
01942     const char *s;
01943     if (!trans) return TXN_TYPE_NONE;
01944     s = kvp_frame_get_string (trans->inst.kvp_data, TRANS_TXN_TYPE_KVP);
01945     if (s) return *s;
01946 
01947     return TXN_TYPE_NONE;
01948 }
01949 
01950 const char *
01951 xaccTransGetReadOnly (const Transaction *trans)
01952 {
01953     /* XXX This flag should be cached in the transaction structure
01954      * for performance reasons, since its checked every trans commit.
01955      */
01956     return trans ? kvp_frame_get_string (
01957                trans->inst.kvp_data, TRANS_READ_ONLY_REASON) : NULL;
01958 }
01959 
01960 gboolean xaccTransIsReadonlyByPostedDate(const Transaction *trans)
01961 {
01962     GDate *threshold_date;
01963     GDate trans_date;
01964     const QofBook *book = xaccTransGetBook (trans);
01965     gboolean result;
01966     g_assert(trans);
01967 
01968     if (!qof_book_uses_autoreadonly(book))
01969     {
01970         return FALSE;
01971     }
01972 
01973     threshold_date = qof_book_get_autoreadonly_gdate(book);
01974     g_assert(threshold_date); // ok because we checked uses_autoreadonly before
01975     trans_date = xaccTransGetDatePostedGDate(trans);
01976 
01977 //    g_warning("there is auto-read-only with days=%d, trans_date_day=%d, threshold_date_day=%d",
01978 //              qof_book_get_num_days_autofreeze(book),
01979 //              g_date_get_day(&trans_date),
01980 //              g_date_get_day(threshold_date));
01981 
01982     if (g_date_compare(&trans_date, threshold_date) < 0)
01983     {
01984         //g_warning("we are auto-read-only");
01985         result = TRUE;
01986     }
01987     else
01988     {
01989         result = FALSE;
01990     }
01991     g_date_free(threshold_date);
01992     return result;
01993 }
01994 
01995 
01996 gboolean
01997 xaccTransHasReconciledSplitsByAccount (const Transaction *trans,
01998                                        const Account *account)
01999 {
02000     GList *node;
02001 
02002     for (node = xaccTransGetSplitList (trans); node; node = node->next)
02003     {
02004         Split *split = node->data;
02005 
02006         if (!xaccTransStillHasSplit(trans, split))
02007             continue;
02008         if (account && (xaccSplitGetAccount(split) != account))
02009             continue;
02010 
02011         switch (xaccSplitGetReconcile (split))
02012         {
02013         case YREC:
02014         case FREC:
02015             return TRUE;
02016 
02017         default:
02018             break;
02019         }
02020     }
02021 
02022     return FALSE;
02023 }
02024 
02025 gboolean
02026 xaccTransHasReconciledSplits (const Transaction *trans)
02027 {
02028     return xaccTransHasReconciledSplitsByAccount (trans, NULL);
02029 }
02030 
02031 
02032 gboolean
02033 xaccTransHasSplitsInStateByAccount (const Transaction *trans,
02034                                     const char state,
02035                                     const Account *account)
02036 {
02037     GList *node;
02038 
02039     for (node = xaccTransGetSplitList (trans); node; node = node->next)
02040     {
02041         Split *split = node->data;
02042 
02043         if (!xaccTransStillHasSplit(trans, split))
02044             continue;
02045         if (account && (xaccSplitGetAccount(split) != account))
02046             continue;
02047 
02048         if (split->reconciled == state)
02049             return TRUE;
02050     }
02051 
02052     return FALSE;
02053 }
02054 
02055 gboolean
02056 xaccTransHasSplitsInState (const Transaction *trans, const char state)
02057 {
02058     return xaccTransHasSplitsInStateByAccount (trans, state, NULL);
02059 }
02060 
02061 
02062 /********************************************************************\
02063 \********************************************************************/
02064 
02065 
02066 /* ====================================================================== */
02067 
02068 static int
02069 counter_thunk(Transaction *t, void *data)
02070 {
02071     (*((guint*)data))++;
02072     return 0;
02073 }
02074 
02075 guint
02076 gnc_book_count_transactions(QofBook *book)
02077 {
02078     guint count = 0;
02079     xaccAccountTreeForEachTransaction(gnc_book_get_root_account(book),
02080                                       counter_thunk, (void*)&count);
02081     return count;
02082 }
02083 
02084 /********************************************************************\
02085 \********************************************************************/
02086 
02087 void
02088 xaccTransVoid(Transaction *trans, const char *reason)
02089 {
02090     KvpFrame *frame;
02091     KvpValue *val;
02092     Timespec now;
02093     char iso8601_str[ISO_DATELENGTH + 1] = "";
02094 
02095     g_return_if_fail(trans && reason);
02096 
02097     xaccTransBeginEdit(trans);
02098     frame = trans->inst.kvp_data;
02099 
02100     val = kvp_frame_get_slot(frame, trans_notes_str);
02101     kvp_frame_set_slot(frame, void_former_notes_str, val);
02102 
02103     kvp_frame_set_string(frame, trans_notes_str, _("Voided transaction"));
02104     kvp_frame_set_string(frame, void_reason_str, reason);
02105 
02106     now.tv_sec = time(NULL);
02107     now.tv_nsec = 0;
02108     gnc_timespec_to_iso8601_buff(now, iso8601_str);
02109     kvp_frame_set_string(frame, void_time_str, iso8601_str);
02110 
02111     FOR_EACH_SPLIT(trans, xaccSplitVoid(s));
02112 
02113     /* Dirtying taken care of by SetReadOnly */
02114     xaccTransSetReadOnly(trans, _("Transaction Voided"));
02115     xaccTransCommitEdit(trans);
02116 }
02117 
02118 gboolean
02119 xaccTransGetVoidStatus(const Transaction *trans)
02120 {
02121     g_return_val_if_fail(trans, FALSE);
02122     return (kvp_frame_get_slot(trans->inst.kvp_data, void_reason_str) != NULL);
02123 }
02124 
02125 const char *
02126 xaccTransGetVoidReason(const Transaction *trans)
02127 {
02128     g_return_val_if_fail(trans, NULL);
02129     return kvp_frame_get_string(trans->inst.kvp_data, void_reason_str);
02130 }
02131 
02132 Timespec
02133 xaccTransGetVoidTime(const Transaction *tr)
02134 {
02135     const char *val;
02136     Timespec void_time = {0, 0};
02137 
02138     g_return_val_if_fail(tr, void_time);
02139 
02140     val = kvp_frame_get_string(tr->inst.kvp_data, void_time_str);
02141     return val ? gnc_iso8601_to_timespec_gmt(val) : void_time;
02142 }
02143 
02144 void
02145 xaccTransUnvoid (Transaction *trans)
02146 {
02147     KvpFrame *frame;
02148     KvpValue *val;
02149 
02150     g_return_if_fail(trans);
02151 
02152     frame = trans->inst.kvp_data;
02153     val = kvp_frame_get_slot(frame, void_reason_str);
02154     if (!val) return; /* Transaction isn't voided. Bail. */
02155 
02156     xaccTransBeginEdit(trans);
02157 
02158     val = kvp_frame_get_slot(frame, void_former_notes_str);
02159     kvp_frame_set_slot(frame, trans_notes_str, val);
02160     kvp_frame_set_slot_nc(frame, void_former_notes_str, NULL);
02161     kvp_frame_set_slot_nc(frame, void_reason_str, NULL);
02162     kvp_frame_set_slot_nc(frame, void_time_str, NULL);
02163 
02164     FOR_EACH_SPLIT(trans, xaccSplitUnvoid(s));
02165 
02166     /* Dirtying taken care of by ClearReadOnly */
02167     xaccTransClearReadOnly(trans);
02168     xaccTransCommitEdit(trans);
02169 }
02170 
02171 Transaction *
02172 xaccTransReverse (Transaction *orig)
02173 {
02174     Transaction *trans;
02175     kvp_value *kvp_val;
02176     g_return_val_if_fail(orig, NULL);
02177 
02178     trans = xaccTransClone(orig);
02179     xaccTransBeginEdit(trans);
02180 
02181     /* Reverse the values on each split. Clear per-split info. */
02182     FOR_EACH_SPLIT(trans,
02183     {
02184         xaccSplitSetAmount(s, gnc_numeric_neg(xaccSplitGetAmount(s)));
02185         xaccSplitSetValue(s, gnc_numeric_neg(xaccSplitGetValue(s)));
02186         xaccSplitSetReconcile(s, NREC);
02187         qof_instance_set_dirty(QOF_INSTANCE(trans));
02188     });
02189 
02190     /* Now update the original with a pointer to the new one */
02191     kvp_val = kvp_value_new_guid(xaccTransGetGUID(trans));
02192     kvp_frame_set_slot_nc(orig->inst.kvp_data, TRANS_REVERSED_BY, kvp_val);
02193 
02194     xaccTransCommitEdit(trans);
02195     return trans;
02196 }
02197 
02198 Transaction *
02199 xaccTransGetReversedBy(const Transaction *trans)
02200 {
02201     GncGUID *guid;
02202 
02203     g_return_val_if_fail(trans, NULL);
02204     guid = kvp_frame_get_guid(trans->inst.kvp_data, TRANS_REVERSED_BY);
02205     return xaccTransLookup(guid, qof_instance_get_book(trans));
02206 }
02207 
02208 void
02209 xaccTransScrubSplits (Transaction *trans)
02210 {
02211     gnc_commodity *currency;
02212 
02213     if (!trans) return;
02214 
02215     xaccTransBeginEdit(trans);
02216     /* The split scrub expects the transaction to have a currency! */
02217     currency = xaccTransGetCurrency (trans);
02218     if (!currency)
02219         PERR ("Transaction doesn't have a currency!");
02220 
02221     FOR_EACH_SPLIT(trans, xaccSplitScrub(s));
02222     xaccTransCommitEdit(trans);
02223 }
02224 
02225 /* ============================================================== */
02238 static void
02239 xaccTransScrubGainsDate (Transaction *trans)
02240 {
02241     SplitList *node;
02242     Timespec ts = {0, 0};
02243 //restart_search:
02244     for (node = trans->splits; node; node = node->next)
02245     {
02246         Split *s = node->data;
02247 
02248         if (!xaccTransStillHasSplit(trans, s)) continue;
02249         xaccSplitDetermineGainStatus(s);
02250 
02251         if ((GAINS_STATUS_GAINS & s->gains) &&
02252                 s->gains_split &&
02253                 ((s->gains_split->gains & GAINS_STATUS_DATE_DIRTY) ||
02254                  (s->gains & GAINS_STATUS_DATE_DIRTY)))
02255         {
02256             Transaction *source_trans = s->gains_split->parent;
02257             ts = source_trans->date_posted;
02258             s->gains &= ~GAINS_STATUS_DATE_DIRTY;
02259             s->gains_split->gains &= ~GAINS_STATUS_DATE_DIRTY;
02260 
02261             xaccTransSetDatePostedTS(trans, &ts);
02262             FOR_EACH_SPLIT(trans, s->gains &= ~GAINS_STATUS_DATE_DIRTY);
02263             //goto restart_search;
02264         }
02265     }
02266 }
02267 
02268 /* ============================================================== */
02269 
02270 void
02271 xaccTransScrubGains (Transaction *trans, Account *gain_acc)
02272 {
02273     SplitList *node;
02274 
02275     ENTER("(trans=%p)", trans);
02276     /* Lock down posted date, its to be synced to the posted date
02277      * for the source of the cap gains. */
02278     xaccTransScrubGainsDate(trans);
02279 
02280     /* Fix up the split amount */
02281 restart:
02282     for (node = trans->splits; node; node = node->next)
02283     {
02284         Split *s = node->data;
02285 
02286         if (!xaccTransStillHasSplit(trans, s)) continue;
02287 
02288         xaccSplitDetermineGainStatus(s);
02289         if (s->gains & GAINS_STATUS_ADIRTY)
02290         {
02291             gboolean altered = FALSE;
02292             s->gains &= ~GAINS_STATUS_ADIRTY;
02293             if (s->lot)
02294                 altered = xaccScrubLot(s->lot);
02295             else
02296                 altered = xaccSplitAssign(s);
02297             if (altered) goto restart;
02298         }
02299     }
02300 
02301     /* Fix up gains split value */
02302     FOR_EACH_SPLIT(trans,
02303                    if ((s->gains & GAINS_STATUS_VDIRTY) ||
02304                        (s->gains_split &&
02305                         (s->gains_split->gains & GAINS_STATUS_VDIRTY)))
02306                    xaccSplitComputeCapGains(s, gain_acc);
02307                   );
02308 
02309     LEAVE("(trans=%p)", trans);
02310 }
02311 
02312 Split *
02313 xaccTransFindSplitByAccount(const Transaction *trans, const Account *acc)
02314 {
02315     if (!trans || !acc) return NULL;
02316     FOR_EACH_SPLIT(trans, if (xaccSplitGetAccount(s) == acc) return s);
02317     return NULL;
02318 }
02319 
02320 /********************************************************************\
02321 \********************************************************************/
02322 /* QofObject function implementation */
02323 
02324 static void
02325 destroy_tx_on_book_close(QofInstance *ent, gpointer data)
02326 {
02327     Transaction* tx = GNC_TRANSACTION(ent);
02328 
02329     xaccTransDestroy(tx);
02330 }
02331 
02336 static void
02337 gnc_transaction_book_end(QofBook* book)
02338 {
02339     QofCollection *col;
02340 
02341     col = qof_book_get_collection(book, GNC_ID_TRANS);
02342     qof_collection_foreach(col, destroy_tx_on_book_close, NULL);
02343 }
02344 
02345 #ifdef _MSC_VER
02346 /* MSVC compiler doesn't have C99 "designated initializers"
02347  * so we wrap them in a macro that is empty on MSVC. */
02348 # define DI(x) /* */
02349 #else
02350 # define DI(x) x
02351 #endif
02352 
02353 /* Hook into the QofObject registry */
02354 static QofObject trans_object_def =
02355 {
02356     DI(.interface_version = ) QOF_OBJECT_VERSION,
02357     DI(.e_type            = ) GNC_ID_TRANS,
02358     DI(.type_label        = ) "Transaction",
02359     DI(.create            = ) (gpointer)xaccMallocTransaction,
02360     DI(.book_begin        = ) NULL,
02361     DI(.book_end          = ) gnc_transaction_book_end,
02362     DI(.is_dirty          = ) qof_collection_is_dirty,
02363     DI(.mark_clean        = ) qof_collection_mark_clean,
02364     DI(.foreach           = ) qof_collection_foreach,
02365     DI(.printable         = ) (const char * (*)(gpointer)) xaccTransGetDescription,
02366     DI(.version_cmp       = ) (int (*)(gpointer, gpointer)) qof_instance_version_cmp,
02367 };
02368 
02369 static gboolean
02370 trans_is_balanced_p (const Transaction *trans)
02371 {
02372     return trans ? xaccTransIsBalanced(trans) : FALSE;
02373 }
02374 
02375 gboolean xaccTransRegister (void)
02376 {
02377     static QofParam params[] =
02378     {
02379         {
02380             TRANS_NUM, QOF_TYPE_STRING,
02381             (QofAccessFunc)xaccTransGetNum,
02382             (QofSetterFunc)qofTransSetNum,
02383             qof_string_number_compare_func
02384         },
02385         {
02386             TRANS_DESCRIPTION, QOF_TYPE_STRING,
02387             (QofAccessFunc)xaccTransGetDescription,
02388             (QofSetterFunc)qofTransSetDescription
02389         },
02390         {
02391             TRANS_DATE_ENTERED, QOF_TYPE_DATE,
02392             (QofAccessFunc)xaccTransRetDateEnteredTS,
02393             (QofSetterFunc)qofTransSetDateEntered
02394         },
02395         {
02396             TRANS_DATE_POSTED, QOF_TYPE_DATE,
02397             (QofAccessFunc)xaccTransRetDatePostedTS,
02398             (QofSetterFunc)qofTransSetDatePosted
02399         },
02400         {
02401             TRANS_DATE_DUE, QOF_TYPE_DATE,
02402             (QofAccessFunc)xaccTransRetDateDueTS, NULL
02403         },
02404         {
02405             TRANS_IMBALANCE, QOF_TYPE_NUMERIC,
02406             (QofAccessFunc)xaccTransGetImbalanceValue, NULL
02407         },
02408         {
02409             TRANS_NOTES, QOF_TYPE_STRING,
02410             (QofAccessFunc)xaccTransGetNotes,
02411             (QofSetterFunc)qofTransSetNotes
02412         },
02413         {
02414             TRANS_IS_CLOSING, QOF_TYPE_BOOLEAN,
02415             (QofAccessFunc)xaccTransGetIsClosingTxn, NULL
02416         },
02417         {
02418             TRANS_IS_BALANCED, QOF_TYPE_BOOLEAN,
02419             (QofAccessFunc)trans_is_balanced_p, NULL
02420         },
02421         {
02422             TRANS_TYPE, QOF_TYPE_CHAR,
02423             (QofAccessFunc)xaccTransGetTxnType,
02424             (QofSetterFunc)xaccTransSetTxnType
02425         },
02426         {
02427             TRANS_VOID_STATUS, QOF_TYPE_BOOLEAN,
02428             (QofAccessFunc)xaccTransGetVoidStatus, NULL
02429         },
02430         {
02431             TRANS_VOID_REASON, QOF_TYPE_STRING,
02432             (QofAccessFunc)xaccTransGetVoidReason, NULL
02433         },
02434         {
02435             TRANS_VOID_TIME, QOF_TYPE_DATE,
02436             (QofAccessFunc)xaccTransGetVoidTime, NULL
02437         },
02438         {
02439             TRANS_SPLITLIST, GNC_ID_SPLIT,
02440             (QofAccessFunc)xaccTransGetSplitList, NULL
02441         },
02442         {
02443             TRANS_KVP, QOF_TYPE_KVP,
02444             (QofAccessFunc)qof_instance_get_slots, NULL
02445         },
02446         {
02447             QOF_PARAM_BOOK, QOF_ID_BOOK,
02448             (QofAccessFunc)qof_instance_get_book, NULL
02449         },
02450         {
02451             QOF_PARAM_GUID, QOF_TYPE_GUID,
02452             (QofAccessFunc)qof_entity_get_guid, NULL
02453         },
02454         { NULL },
02455     };
02456 
02457     qof_class_register (GNC_ID_TRANS, (QofSortFunc)xaccTransOrder, params);
02458 
02459     return qof_object_register (&trans_object_def);
02460 }
02461 
02462 /************************ END OF ************************************\
02463 \************************* FILE *************************************/
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines