|
GnuCash 2.4.99
|
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 *************************************/
1.7.4