|
GnuCash 2.4.99
|
00001 /********************************************************************\ 00002 * Split.c -- split 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 #endif 00036 #include <time.h> 00037 #ifdef HAVE_UNISTD_H 00038 # include <unistd.h> 00039 #endif 00040 00041 #include "qof.h" 00042 #include "Split.h" 00043 #include "AccountP.h" 00044 #include "Scrub.h" 00045 #include "Scrub3.h" 00046 #include "TransactionP.h" 00047 #include "TransLog.h" 00048 #include "cap-gains.h" 00049 #include "gnc-commodity.h" 00050 #include "gnc-engine.h" 00051 #include "gnc-lot.h" 00052 #include "gnc-event.h" 00053 00054 const char *void_former_amt_str = "void-former-amount"; 00055 const char *void_former_val_str = "void-former-value"; 00056 00057 #define PRICE_SIGFIGS 6 00058 00059 /* This static indicates the debugging module that this .o belongs to. */ 00060 static QofLogModule log_module = GNC_MOD_ENGINE; 00061 00062 enum 00063 { 00064 PROP_0, 00065 PROP_ACTION, 00066 PROP_MEMO, 00067 PROP_VALUE, 00068 PROP_AMOUNT, 00069 PROP_RECONCILE_DATE, 00070 PROP_TX, 00071 PROP_ACCOUNT, 00072 PROP_LOT 00073 }; 00074 00075 /* GObject Initialization */ 00076 G_DEFINE_TYPE(Split, gnc_split, QOF_TYPE_INSTANCE) 00077 00078 static void 00079 gnc_split_init(Split* split) 00080 { 00081 /* fill in some sane defaults */ 00082 split->acc = NULL; 00083 split->orig_acc = NULL; 00084 split->parent = NULL; 00085 split->lot = NULL; 00086 00087 split->action = CACHE_INSERT(""); 00088 split->memo = CACHE_INSERT(""); 00089 split->reconciled = NREC; 00090 split->amount = gnc_numeric_zero(); 00091 split->value = gnc_numeric_zero(); 00092 00093 split->date_reconciled.tv_sec = 0; 00094 split->date_reconciled.tv_nsec = 0; 00095 00096 split->balance = gnc_numeric_zero(); 00097 split->cleared_balance = gnc_numeric_zero(); 00098 split->reconciled_balance = gnc_numeric_zero(); 00099 00100 split->gains = GAINS_STATUS_UNKNOWN; 00101 split->gains_split = NULL; 00102 } 00103 00104 static void 00105 gnc_split_dispose(GObject *splitp) 00106 { 00107 G_OBJECT_CLASS(gnc_split_parent_class)->dispose(splitp); 00108 } 00109 00110 static void 00111 gnc_split_finalize(GObject* splitp) 00112 { 00113 G_OBJECT_CLASS(gnc_split_parent_class)->finalize(splitp); 00114 } 00115 00116 static void 00117 gnc_split_get_property(GObject *object, 00118 guint prop_id, 00119 GValue *value, 00120 GParamSpec *pspec) 00121 { 00122 Split *split; 00123 00124 g_return_if_fail(GNC_IS_SPLIT(object)); 00125 00126 split = GNC_SPLIT(object); 00127 switch (prop_id) 00128 { 00129 case PROP_ACTION: 00130 g_value_set_string(value, split->action); 00131 break; 00132 case PROP_MEMO: 00133 g_value_set_string(value, split->memo); 00134 break; 00135 case PROP_VALUE: 00136 g_value_set_boxed(value, &split->value); 00137 break; 00138 case PROP_AMOUNT: 00139 g_value_set_boxed(value, &split->amount); 00140 break; 00141 case PROP_RECONCILE_DATE: 00142 g_value_set_boxed(value, &split->date_reconciled); 00143 break; 00144 case PROP_TX: 00145 g_value_set_object(value, split->parent); 00146 break; 00147 case PROP_ACCOUNT: 00148 g_value_set_object(value, split->acc); 00149 break; 00150 case PROP_LOT: 00151 g_value_set_object(value, split->lot); 00152 break; 00153 default: 00154 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); 00155 break; 00156 } 00157 } 00158 00159 static void 00160 gnc_split_set_property(GObject *object, 00161 guint prop_id, 00162 const GValue *value, 00163 GParamSpec *pspec) 00164 { 00165 Split *split; 00166 gnc_numeric* number; 00167 00168 g_return_if_fail(GNC_IS_SPLIT(object)); 00169 00170 split = GNC_SPLIT(object); 00171 switch (prop_id) 00172 { 00173 case PROP_ACTION: 00174 xaccSplitSetAction(split, g_value_get_string(value)); 00175 break; 00176 case PROP_MEMO: 00177 xaccSplitSetMemo(split, g_value_get_string(value)); 00178 break; 00179 case PROP_VALUE: 00180 number = g_value_get_boxed(value); 00181 xaccSplitSetValue(split, *number); 00182 break; 00183 case PROP_AMOUNT: 00184 number = g_value_get_boxed(value); 00185 xaccSplitSetAmount(split, *number); 00186 break; 00187 case PROP_RECONCILE_DATE: 00188 xaccSplitSetDateReconciledTS(split, g_value_get_boxed(value)); 00189 break; 00190 case PROP_TX: 00191 xaccSplitSetParent(split, g_value_get_object(value)); 00192 break; 00193 case PROP_ACCOUNT: 00194 xaccSplitSetAccount(split, g_value_get_object(value)); 00195 break; 00196 case PROP_LOT: 00197 xaccSplitSetLot(split, g_value_get_object(value)); 00198 break; 00199 default: 00200 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); 00201 break; 00202 } 00203 } 00204 00205 static void 00206 gnc_split_class_init(SplitClass* klass) 00207 { 00208 GObjectClass* gobject_class = G_OBJECT_CLASS(klass); 00209 00210 gobject_class->dispose = gnc_split_dispose; 00211 gobject_class->finalize = gnc_split_finalize; 00212 gobject_class->set_property = gnc_split_set_property; 00213 gobject_class->get_property = gnc_split_get_property; 00214 00215 g_object_class_install_property 00216 (gobject_class, 00217 PROP_ACTION, 00218 g_param_spec_string("action", 00219 "Action", 00220 "The action is an arbitrary string assigned " 00221 "by the user. It is intended to be a short " 00222 "string that contains extra information about " 00223 "this split.", 00224 NULL, 00225 G_PARAM_READWRITE)); 00226 00227 g_object_class_install_property 00228 (gobject_class, 00229 PROP_MEMO, 00230 g_param_spec_string("memo", 00231 "Memo", 00232 "The action is an arbitrary string assigned " 00233 "by the user. It is intended to be a short " 00234 "string that describes the purpose of " 00235 "this split.", 00236 NULL, 00237 G_PARAM_READWRITE)); 00238 00239 g_object_class_install_property 00240 (gobject_class, 00241 PROP_VALUE, 00242 g_param_spec_boxed("value", 00243 "Split Value", 00244 "The value for this split in the common currency. " 00245 "The value and the amount provide enough information to " 00246 "calculate a conversion rate.", 00247 GNC_TYPE_NUMERIC, 00248 G_PARAM_READWRITE)); 00249 00250 g_object_class_install_property 00251 (gobject_class, 00252 PROP_AMOUNT, 00253 g_param_spec_boxed("amount", 00254 "Split Amount", 00255 "The value for this split in the currency of its account. " 00256 "The value and the amount provide enough information to " 00257 "calculate a conversion rate.", 00258 GNC_TYPE_NUMERIC, 00259 G_PARAM_READWRITE)); 00260 00261 g_object_class_install_property 00262 (gobject_class, 00263 PROP_RECONCILE_DATE, 00264 g_param_spec_boxed("reconcile-date", 00265 "Reconcile Date", 00266 "The date this split was reconciled.", 00267 GNC_TYPE_TIMESPEC, 00268 G_PARAM_READWRITE)); 00269 00270 g_object_class_install_property 00271 (gobject_class, 00272 PROP_TX, 00273 g_param_spec_object ("transaction", 00274 "Transaction", 00275 "The transaction that this split belongs to.", 00276 GNC_TYPE_TRANSACTION, 00277 G_PARAM_READWRITE)); 00278 00279 g_object_class_install_property 00280 (gobject_class, 00281 PROP_ACCOUNT, 00282 g_param_spec_object ("account", 00283 "Account", 00284 "The account that this split belongs to.", 00285 GNC_TYPE_ACCOUNT, 00286 G_PARAM_READWRITE)); 00287 00288 g_object_class_install_property 00289 (gobject_class, 00290 PROP_LOT, 00291 g_param_spec_object ("lot", 00292 "Lot", 00293 "The lot that this split belongs to.", 00294 GNC_TYPE_LOT, 00295 G_PARAM_READWRITE)); 00296 } 00297 00298 /********************************************************************\ 00299 * xaccInitSplit 00300 * Initialize a Split structure 00301 \********************************************************************/ 00302 00303 static void 00304 xaccInitSplit(Split * split, QofBook *book) 00305 { 00306 qof_instance_init_data(&split->inst, GNC_ID_SPLIT, book); 00307 } 00308 00309 void 00310 xaccSplitReinit(Split * split) 00311 { 00312 /* fill in some sane defaults */ 00313 split->acc = NULL; 00314 split->orig_acc = NULL; 00315 split->parent = NULL; 00316 split->lot = NULL; 00317 00318 CACHE_REPLACE(split->action, ""); 00319 CACHE_REPLACE(split->memo, ""); 00320 split->reconciled = NREC; 00321 split->amount = gnc_numeric_zero(); 00322 split->value = gnc_numeric_zero(); 00323 00324 split->date_reconciled.tv_sec = 0; 00325 split->date_reconciled.tv_nsec = 0; 00326 00327 split->balance = gnc_numeric_zero(); 00328 split->cleared_balance = gnc_numeric_zero(); 00329 split->reconciled_balance = gnc_numeric_zero(); 00330 00331 if (split->inst.kvp_data) 00332 kvp_frame_delete(split->inst.kvp_data); 00333 split->inst.kvp_data = kvp_frame_new(); 00334 qof_instance_set_idata(split, 0); 00335 00336 split->gains = GAINS_STATUS_UNKNOWN; 00337 split->gains_split = NULL; 00338 } 00339 00340 /********************************************************************\ 00341 \********************************************************************/ 00342 00343 Split * 00344 xaccMallocSplit(QofBook *book) 00345 { 00346 Split *split; 00347 g_return_val_if_fail (book, NULL); 00348 00349 split = g_object_new (GNC_TYPE_SPLIT, NULL); 00350 xaccInitSplit (split, book); 00351 00352 return split; 00353 } 00354 00355 /********************************************************************\ 00356 \********************************************************************/ 00357 /* This routine is not exposed externally, since it does weird things, 00358 * like not really setting up the parent account correctly, and ditto 00359 * the parent transaction. This routine is prone to programmer error 00360 * if not used correctly. It is used only by the edit-rollback code. 00361 * Don't get duped! 00362 */ 00363 00364 Split * 00365 xaccDupeSplit (const Split *s) 00366 { 00367 Split *split = g_object_new (GNC_TYPE_SPLIT, NULL); 00368 00369 /* Trash the entity table. We don't want to mistake the cloned 00370 * splits as something official. If we ever use this split, we'll 00371 * have to fix this up. 00372 */ 00373 split->inst.e_type = NULL; 00374 qof_instance_copy_guid(split, s); 00375 qof_instance_copy_book(split, s); 00376 00377 split->parent = s->parent; 00378 split->acc = s->acc; 00379 split->orig_acc = s->orig_acc; 00380 split->lot = s->lot; 00381 00382 split->memo = CACHE_INSERT(s->memo); 00383 split->action = CACHE_INSERT(s->action); 00384 00385 split->inst.kvp_data = kvp_frame_copy (s->inst.kvp_data); 00386 00387 split->reconciled = s->reconciled; 00388 split->date_reconciled = s->date_reconciled; 00389 00390 split->value = s->value; 00391 split->amount = s->amount; 00392 00393 /* no need to futz with the balances; these get wiped each time ... 00394 * split->balance = s->balance; 00395 * split->cleared_balance = s->cleared_balance; 00396 * split->reconciled_balance = s->reconciled_balance; 00397 */ 00398 00399 return split; 00400 } 00401 00402 Split * 00403 xaccSplitClone (const Split *s) 00404 { 00405 Split *split = g_object_new (GNC_TYPE_SPLIT, NULL); 00406 00407 split->parent = NULL; 00408 split->memo = CACHE_INSERT(s->memo); 00409 split->action = CACHE_INSERT(s->action); 00410 split->reconciled = s->reconciled; 00411 split->date_reconciled = s->date_reconciled; 00412 split->value = s->value; 00413 split->amount = s->amount; 00414 split->balance = s->balance; 00415 split->cleared_balance = s->cleared_balance; 00416 split->reconciled_balance = s->reconciled_balance; 00417 00418 split->gains = GAINS_STATUS_UNKNOWN; 00419 split->gains_split = NULL; 00420 00421 qof_instance_init_data(&split->inst, GNC_ID_SPLIT, qof_instance_get_book(s)); 00422 kvp_frame_delete(split->inst.kvp_data); 00423 split->inst.kvp_data = kvp_frame_copy(s->inst.kvp_data); 00424 00425 xaccAccountInsertSplit(s->acc, split); 00426 if (s->lot) 00427 { 00428 /* CHECKME: Is this right? */ 00429 gnc_lot_add_split(s->lot, split); 00430 } 00431 return split; 00432 } 00433 00434 #ifdef DUMP_FUNCTIONS 00435 void 00436 xaccSplitDump (const Split *split, const char *tag) 00437 { 00438 printf(" %s Split %p", tag, split); 00439 printf(" Book: %p\n", qof_instance_get_book(split)); 00440 printf(" Account: %p (%s)\n", split->acc, 00441 split->acc ? xaccAccountGetName(split->acc) : ""); 00442 printf(" Commod: %s\n", 00443 split->acc ? 00444 gnc_commodity_get_printname(xaccAccountGetCommodity(split->acc)) 00445 : ""); 00446 printf(" Lot: %p\n", split->lot); 00447 printf(" Parent: %p\n", split->parent); 00448 printf(" Gains: %p\n", split->gains_split); 00449 printf(" Memo: %s\n", split->memo ? split->memo : "(null)"); 00450 printf(" Action: %s\n", split->action ? split->action : "(null)"); 00451 printf(" KVP Data: %p\n", split->inst.kvp_data); 00452 printf(" Recncld: %c (date %s)\n", split->reconciled, 00453 gnc_print_date(split->date_reconciled)); 00454 printf(" Value: %s\n", gnc_numeric_to_string(split->value)); 00455 printf(" Amount: %s\n", gnc_numeric_to_string(split->amount)); 00456 printf(" Balance: %s\n", gnc_numeric_to_string(split->balance)); 00457 printf(" CBalance: %s\n", gnc_numeric_to_string(split->cleared_balance)); 00458 printf(" RBalance: %s\n", 00459 gnc_numeric_to_string(split->reconciled_balance)); 00460 printf(" idata: %x\n", qof_instance_get_idata(split)); 00461 } 00462 #endif 00463 00464 /********************************************************************\ 00465 \********************************************************************/ 00466 00467 void 00468 xaccFreeSplit (Split *split) 00469 { 00470 if (!split) return; 00471 00472 /* Debug double-free's */ 00473 if (((char *) 1) == split->memo) 00474 { 00475 PERR ("double-free %p", split); 00476 return; 00477 } 00478 CACHE_REMOVE(split->memo); 00479 CACHE_REMOVE(split->action); 00480 00481 /* Just in case someone looks up freed memory ... */ 00482 split->memo = (char *) 1; 00483 split->action = NULL; 00484 split->reconciled = NREC; 00485 split->amount = gnc_numeric_zero(); 00486 split->value = gnc_numeric_zero(); 00487 split->parent = NULL; 00488 split->lot = NULL; 00489 split->acc = NULL; 00490 split->orig_acc = NULL; 00491 00492 split->date_reconciled.tv_sec = 0; 00493 split->date_reconciled.tv_nsec = 0; 00494 00495 // Is this right? 00496 if (split->gains_split) split->gains_split->gains_split = NULL; 00497 /* qof_instance_release(&split->inst); */ 00498 g_object_unref(split); 00499 } 00500 00501 void mark_split (Split *s) 00502 { 00503 if (s->acc) 00504 { 00505 g_object_set(s->acc, "sort-dirty", TRUE, "balance-dirty", TRUE, NULL); 00506 } 00507 00508 /* set dirty flag on lot too. */ 00509 if (s->lot) gnc_lot_set_closed_unknown(s->lot); 00510 } 00511 00512 /* 00513 * Helper routine for xaccSplitEqual. 00514 */ 00515 static gboolean 00516 xaccSplitEqualCheckBal (const char *tag, gnc_numeric a, gnc_numeric b) 00517 { 00518 char *str_a, *str_b; 00519 00520 if (gnc_numeric_equal (a, b)) 00521 return TRUE; 00522 00523 str_a = gnc_numeric_to_string (a); 00524 str_b = gnc_numeric_to_string (b); 00525 00526 PWARN ("%sbalances differ: %s vs %s", tag, str_a, str_b); 00527 00528 g_free (str_a); 00529 g_free (str_b); 00530 00531 return FALSE; 00532 } 00533 00534 /******************************************************************** 00535 * xaccSplitEqual 00536 ********************************************************************/ 00537 gboolean 00538 xaccSplitEqual(const Split *sa, const Split *sb, 00539 gboolean check_guids, 00540 gboolean check_balances, 00541 gboolean check_txn_splits) 00542 { 00543 gboolean same_book; 00544 00545 if (!sa && !sb) return TRUE; /* Arguable. FALSE is better, methinks */ 00546 00547 if (!sa || !sb) 00548 { 00549 PWARN ("one is NULL"); 00550 return FALSE; 00551 } 00552 00553 if (sa == sb) return TRUE; 00554 00555 same_book = qof_instance_get_book(QOF_INSTANCE(sa)) == qof_instance_get_book(QOF_INSTANCE(sb)); 00556 00557 if (check_guids) 00558 { 00559 if (qof_instance_guid_compare(sa, sb) != 0) 00560 { 00561 PWARN ("GUIDs differ"); 00562 return FALSE; 00563 } 00564 } 00565 00566 /* If the same book, since these strings are cached we can just use pointer equality */ 00567 if ((same_book && sa->memo != sb->memo) || (!same_book && safe_strcmp(sa->memo, sb->memo) != 0)) 00568 { 00569 PWARN ("memos differ: (%p)%s vs (%p)%s", 00570 sa->memo, sa->memo, sb->memo, sb->memo); 00571 return FALSE; 00572 } 00573 00574 if ((same_book && sa->action != sb->action) || (!same_book && safe_strcmp(sa->action, sb->action) != 0)) 00575 { 00576 PWARN ("actions differ: %s vs %s", sa->action, sb->action); 00577 return FALSE; 00578 } 00579 00580 if (kvp_frame_compare(sa->inst.kvp_data, sb->inst.kvp_data) != 0) 00581 { 00582 char *frame_a; 00583 char *frame_b; 00584 00585 frame_a = kvp_frame_to_string (sa->inst.kvp_data); 00586 frame_b = kvp_frame_to_string (sb->inst.kvp_data); 00587 00588 PWARN ("kvp frames differ:\n%s\n\nvs\n\n%s", frame_a, frame_b); 00589 00590 g_free (frame_a); 00591 g_free (frame_b); 00592 00593 return FALSE; 00594 } 00595 00596 if (sa->reconciled != sb->reconciled) 00597 { 00598 PWARN ("reconcile flags differ: %c vs %c", sa->reconciled, sb->reconciled); 00599 return FALSE; 00600 } 00601 00602 if (timespec_cmp(&(sa->date_reconciled), &(sb->date_reconciled))) 00603 { 00604 PWARN ("reconciled date differs"); 00605 return FALSE; 00606 } 00607 00608 if (!gnc_numeric_eq(xaccSplitGetAmount (sa), xaccSplitGetAmount (sb))) 00609 { 00610 char *str_a; 00611 char *str_b; 00612 00613 str_a = gnc_numeric_to_string (xaccSplitGetAmount (sa)); 00614 str_b = gnc_numeric_to_string (xaccSplitGetAmount (sb)); 00615 00616 PWARN ("amounts differ: %s vs %s", str_a, str_b); 00617 00618 g_free (str_a); 00619 g_free (str_b); 00620 00621 return FALSE; 00622 } 00623 00624 if (!gnc_numeric_eq(xaccSplitGetValue (sa), xaccSplitGetValue (sb))) 00625 { 00626 char *str_a; 00627 char *str_b; 00628 00629 str_a = gnc_numeric_to_string (xaccSplitGetValue (sa)); 00630 str_b = gnc_numeric_to_string (xaccSplitGetValue (sb)); 00631 00632 PWARN ("values differ: %s vs %s", str_a, str_b); 00633 00634 g_free (str_a); 00635 g_free (str_b); 00636 00637 return FALSE; 00638 } 00639 00640 if (check_balances) 00641 { 00642 if (!xaccSplitEqualCheckBal ("", sa->balance, sb->balance)) 00643 return FALSE; 00644 if (!xaccSplitEqualCheckBal ("cleared ", sa->cleared_balance, 00645 sb->cleared_balance)) 00646 return FALSE; 00647 if (!xaccSplitEqualCheckBal ("reconciled ", sa->reconciled_balance, 00648 sb->reconciled_balance)) 00649 return FALSE; 00650 } 00651 00652 if (!xaccTransEqual(sa->parent, sb->parent, check_guids, check_txn_splits, 00653 check_balances, FALSE)) 00654 { 00655 PWARN ("transactions differ"); 00656 return FALSE; 00657 } 00658 00659 return TRUE; 00660 } 00661 00662 /******************************************************************** 00663 * Account funcs 00664 ********************************************************************/ 00665 00666 Account * 00667 xaccSplitGetAccount (const Split *s) 00668 { 00669 return s ? s->acc : NULL; 00670 } 00671 00672 void 00673 xaccSplitSetAccount (Split *s, Account *acc) 00674 { 00675 Transaction *trans; 00676 00677 g_return_if_fail(s && acc); 00678 g_return_if_fail(qof_instance_books_equal(acc, s)); 00679 00680 trans = s->parent; 00681 if (trans) 00682 xaccTransBeginEdit(trans); 00683 00684 s->acc = acc; 00685 qof_instance_set_dirty(QOF_INSTANCE(s)); 00686 00687 if (trans) 00688 xaccTransCommitEdit(trans); 00689 } 00690 00691 static void commit_err (QofInstance *inst, QofBackendError errcode) 00692 { 00693 PERR("commit error: %d", errcode); 00694 gnc_engine_signal_commit_error( errcode ); 00695 } 00696 00697 /* An engine-private helper for completing xaccTransCommitEdit(). */ 00698 void 00699 xaccSplitCommitEdit(Split *s) 00700 { 00701 Account *acc = NULL; 00702 Account *orig_acc = NULL; 00703 00704 g_return_if_fail(s); 00705 if (!qof_instance_is_dirty(QOF_INSTANCE(s))) 00706 return; 00707 00708 orig_acc = s->orig_acc; 00709 00710 if (GNC_IS_ACCOUNT(s->acc)) 00711 acc = s->acc; 00712 00713 /* Remove from lot (but only if it hasn't been moved to 00714 new lot already) */ 00715 if (s->lot && (gnc_lot_get_account(s->lot) != acc || qof_instance_get_destroying(s))) 00716 gnc_lot_remove_split (s->lot, s); 00717 00718 /* Possibly remove the split from the original account... */ 00719 if (orig_acc && (orig_acc != acc || qof_instance_get_destroying(s))) 00720 { 00721 if (!gnc_account_remove_split(orig_acc, s)) 00722 { 00723 PERR("Account lost track of moved or deleted split."); 00724 } 00725 } 00726 00727 /* ... and insert it into the new account if needed */ 00728 if (acc && (orig_acc != acc) && !qof_instance_get_destroying(s)) 00729 { 00730 if (gnc_account_insert_split(acc, s)) 00731 { 00732 /* If the split's lot belonged to some other account, we 00733 leave it so. */ 00734 if (s->lot && (NULL == gnc_lot_get_account(s->lot))) 00735 xaccAccountInsertLot (acc, s->lot); 00736 } 00737 else 00738 { 00739 PERR("Account grabbed split prematurely."); 00740 } 00741 xaccSplitSetAmount(s, xaccSplitGetAmount(s)); 00742 } 00743 00744 if (s->parent != s->orig_parent) 00745 { 00746 //FIXME: find better event 00747 if (s->orig_parent) 00748 qof_event_gen(&s->orig_parent->inst, QOF_EVENT_MODIFY, 00749 NULL); 00750 } 00751 if (s->lot) 00752 { 00753 /* A change of value/amnt affects gains display, etc. */ 00754 qof_event_gen (QOF_INSTANCE(s->lot), QOF_EVENT_MODIFY, NULL); 00755 } 00756 00757 /* Important: we save off the original parent transaction and account 00758 so that when we commit, we can generate signals for both the 00759 original and new transactions, for the _next_ begin/commit cycle. */ 00760 s->orig_acc = s->acc; 00761 s->orig_parent = s->parent; 00762 if (!qof_commit_edit_part2(QOF_INSTANCE(s), commit_err, NULL, 00763 (void (*) (QofInstance *)) xaccFreeSplit)) 00764 return; 00765 00766 if (acc) 00767 { 00768 g_object_set(acc, "sort-dirty", TRUE, "balance-dirty", TRUE, NULL); 00769 xaccAccountRecomputeBalance(acc); 00770 } 00771 } 00772 00773 /* An engine-private helper for completing xaccTransRollbackEdit(). */ 00774 void 00775 xaccSplitRollbackEdit(Split *s) 00776 { 00777 00778 /* Don't use setters because we want to allow NULL. This is legit 00779 only because we don't emit events for changing accounts until 00780 the final commit. */ 00781 if (s->acc != s->orig_acc) 00782 s->acc = s->orig_acc; 00783 00784 /* Undestroy if needed */ 00785 if (qof_instance_get_destroying(s) && s->parent) 00786 { 00787 GncEventData ed; 00788 qof_instance_set_destroying(s, FALSE); 00789 ed.node = s; 00790 ed.idx = -1; /* unused */ 00791 qof_event_gen(&s->parent->inst, GNC_EVENT_ITEM_ADDED, &ed); 00792 } 00793 00794 /* But for the parent trans, we want the intermediate events, so 00795 we use the setter. */ 00796 xaccSplitSetParent(s, s->orig_parent); 00797 } 00798 00799 /********************************************************************\ 00800 \********************************************************************/ 00801 00802 Split * 00803 xaccSplitLookup (const GncGUID *guid, QofBook *book) 00804 { 00805 QofCollection *col; 00806 if (!guid || !book) return NULL; 00807 col = qof_book_get_collection (book, GNC_ID_SPLIT); 00808 return (Split *) qof_collection_lookup_entity (col, guid); 00809 } 00810 00811 /********************************************************************\ 00812 \********************************************************************/ 00813 /* Routines for marking splits dirty, and for sending out change 00814 * events. Note that we can't just mark-n-generate-event in one 00815 * step, since sometimes we need to mark things up before its suitable 00816 * to send out a change event. 00817 */ 00818 00819 /* CHECKME: This function modifies the Split without dirtying or 00820 checking its parent. Is that correct? */ 00821 void 00822 xaccSplitDetermineGainStatus (Split *split) 00823 { 00824 Split *other; 00825 KvpValue *val; 00826 00827 if (GAINS_STATUS_UNKNOWN != split->gains) return; 00828 00829 other = xaccSplitGetCapGainsSplit (split); 00830 if (other) 00831 { 00832 split->gains = GAINS_STATUS_A_VDIRTY | GAINS_STATUS_DATE_DIRTY; 00833 split->gains_split = other; 00834 return; 00835 } 00836 00837 val = kvp_frame_get_slot (split->inst.kvp_data, "gains-source"); 00838 if (!val) 00839 { 00840 // CHECKME: We leave split->gains_split alone. Is that correct? 00841 split->gains = GAINS_STATUS_A_VDIRTY | GAINS_STATUS_DATE_DIRTY; 00842 } 00843 else 00844 { 00845 QofCollection *col; 00846 col = qof_book_get_collection (qof_instance_get_book(split), GNC_ID_SPLIT); 00847 split->gains = GAINS_STATUS_GAINS; 00848 other = (Split *) qof_collection_lookup_entity (col, 00849 kvp_value_get_guid (val)); 00850 split->gains_split = other; 00851 } 00852 } 00853 00854 /********************************************************************\ 00855 \********************************************************************/ 00856 00857 static inline int 00858 get_currency_denom(const Split * s) 00859 { 00860 if (!s) 00861 { 00862 return 0; 00863 } 00864 else if (!s->parent || !s->parent->common_currency) 00865 { 00866 return 100000; 00867 } 00868 else 00869 { 00870 return gnc_commodity_get_fraction (s->parent->common_currency); 00871 } 00872 } 00873 00874 static inline int 00875 get_commodity_denom(const Split * s) 00876 { 00877 if (!s) 00878 { 00879 return 0; 00880 } 00881 else if (!s->acc) 00882 { 00883 return 100000; 00884 } 00885 else 00886 { 00887 return xaccAccountGetCommoditySCU(s->acc); 00888 } 00889 } 00890 00891 /******************************************************************** 00892 * xaccSplitGetSlots 00893 ********************************************************************/ 00894 00895 KvpFrame * 00896 xaccSplitGetSlots (const Split * s) 00897 { 00898 return qof_instance_get_slots(QOF_INSTANCE(s)); 00899 } 00900 /* Used for testing only: _get_random_split in test-engine-stuff.c */ 00901 void 00902 xaccSplitSetSlots_nc(Split *s, KvpFrame *frm) 00903 { 00904 if (!s || !frm) return; 00905 xaccTransBeginEdit(s->parent); 00906 qof_instance_set_slots(QOF_INSTANCE(s), frm); 00907 xaccTransCommitEdit(s->parent); 00908 00909 } 00910 00911 /********************************************************************\ 00912 \********************************************************************/ 00913 00914 void 00915 xaccSplitSetSharePriceAndAmount (Split *s, gnc_numeric price, gnc_numeric amt) 00916 { 00917 if (!s) return; 00918 ENTER (" "); 00919 xaccTransBeginEdit (s->parent); 00920 00921 s->amount = gnc_numeric_convert(amt, get_commodity_denom(s), 00922 GNC_HOW_RND_ROUND_HALF_UP); 00923 s->value = gnc_numeric_mul(s->amount, price, 00924 get_currency_denom(s), GNC_HOW_RND_ROUND_HALF_UP); 00925 00926 SET_GAINS_A_VDIRTY(s); 00927 mark_split (s); 00928 qof_instance_set_dirty(QOF_INSTANCE(s)); 00929 xaccTransCommitEdit(s->parent); 00930 LEAVE (""); 00931 } 00932 00933 static void 00934 qofSplitSetSharePrice (Split *split, gnc_numeric price) 00935 { 00936 g_return_if_fail(split); 00937 split->value = gnc_numeric_mul(xaccSplitGetAmount(split), 00938 price, get_currency_denom(split), 00939 GNC_HOW_RND_ROUND_HALF_UP); 00940 } 00941 00942 void 00943 xaccSplitSetSharePrice (Split *s, gnc_numeric price) 00944 { 00945 if (!s) return; 00946 ENTER (" "); 00947 xaccTransBeginEdit (s->parent); 00948 00949 s->value = gnc_numeric_mul(xaccSplitGetAmount(s), 00950 price, get_currency_denom(s), 00951 GNC_HOW_RND_ROUND_HALF_UP); 00952 00953 SET_GAINS_VDIRTY(s); 00954 mark_split (s); 00955 qof_instance_set_dirty(QOF_INSTANCE(s)); 00956 xaccTransCommitEdit(s->parent); 00957 LEAVE (""); 00958 } 00959 00960 static void 00961 qofSplitSetAmount (Split *split, gnc_numeric amt) 00962 { 00963 g_return_if_fail(split); 00964 if (split->acc) 00965 { 00966 split->amount = gnc_numeric_convert(amt, 00967 get_commodity_denom(split), GNC_HOW_RND_ROUND_HALF_UP); 00968 } 00969 else 00970 { 00971 split->amount = amt; 00972 } 00973 } 00974 00975 /* The amount of the split in the _account's_ commodity. */ 00976 void 00977 xaccSplitSetAmount (Split *s, gnc_numeric amt) 00978 { 00979 if (!s) return; 00980 g_return_if_fail(gnc_numeric_check(amt) == GNC_ERROR_OK); 00981 ENTER ("(split=%p) old amt=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT 00982 " new amt=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, s, 00983 s->amount.num, s->amount.denom, amt.num, amt.denom); 00984 00985 xaccTransBeginEdit (s->parent); 00986 if (s->acc) 00987 s->amount = gnc_numeric_convert(amt, get_commodity_denom(s), 00988 GNC_HOW_RND_ROUND_HALF_UP); 00989 else 00990 s->amount = amt; 00991 00992 SET_GAINS_ADIRTY(s); 00993 mark_split (s); 00994 qof_instance_set_dirty(QOF_INSTANCE(s)); 00995 xaccTransCommitEdit(s->parent); 00996 LEAVE(""); 00997 } 00998 00999 static void 01000 qofSplitSetValue (Split *split, gnc_numeric amt) 01001 { 01002 g_return_if_fail(split); 01003 split->value = gnc_numeric_convert(amt, 01004 get_currency_denom(split), GNC_HOW_RND_ROUND_HALF_UP); 01005 } 01006 01007 /* The value of the split in the _transaction's_ currency. */ 01008 void 01009 xaccSplitSetValue (Split *s, gnc_numeric amt) 01010 { 01011 gnc_numeric new_val; 01012 if (!s) return; 01013 01014 g_return_if_fail(gnc_numeric_check(amt) == GNC_ERROR_OK); 01015 ENTER ("(split=%p) old val=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT 01016 " new val=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, s, 01017 s->value.num, s->value.denom, amt.num, amt.denom); 01018 01019 xaccTransBeginEdit (s->parent); 01020 new_val = gnc_numeric_convert(amt, get_currency_denom(s), 01021 GNC_HOW_RND_ROUND_HALF_UP); 01022 if (gnc_numeric_check(new_val) == GNC_ERROR_OK) 01023 s->value = new_val; 01024 else PERR("numeric error in converting the split value's denominator"); 01025 01026 SET_GAINS_VDIRTY(s); 01027 mark_split (s); 01028 qof_instance_set_dirty(QOF_INSTANCE(s)); 01029 xaccTransCommitEdit(s->parent); 01030 LEAVE (""); 01031 } 01032 01033 /********************************************************************\ 01034 \********************************************************************/ 01035 01036 gnc_numeric 01037 xaccSplitGetBalance (const Split *s) 01038 { 01039 return s ? s->balance : gnc_numeric_zero(); 01040 } 01041 01042 gnc_numeric 01043 xaccSplitGetClearedBalance (const Split *s) 01044 { 01045 return s ? s->cleared_balance : gnc_numeric_zero(); 01046 } 01047 01048 gnc_numeric 01049 xaccSplitGetReconciledBalance (const Split *s) 01050 { 01051 return s ? s->reconciled_balance : gnc_numeric_zero(); 01052 } 01053 01054 void 01055 xaccSplitSetBaseValue (Split *s, gnc_numeric value, 01056 const gnc_commodity * base_currency) 01057 { 01058 const gnc_commodity *currency; 01059 const gnc_commodity *commodity; 01060 01061 if (!s) return; 01062 xaccTransBeginEdit (s->parent); 01063 01064 if (!s->acc) 01065 { 01066 PERR ("split must have a parent account"); 01067 return; 01068 } 01069 01070 currency = xaccTransGetCurrency (s->parent); 01071 commodity = xaccAccountGetCommodity (s->acc); 01072 01073 /* If the base_currency is the transaction's commodity ('currency'), 01074 * set the value. If it's the account commodity, set the 01075 * amount. If both, set both. */ 01076 if (gnc_commodity_equiv(currency, base_currency)) 01077 { 01078 if (gnc_commodity_equiv(commodity, base_currency)) 01079 { 01080 s->amount = gnc_numeric_convert(value, 01081 get_commodity_denom(s), 01082 GNC_HOW_RND_ROUND_HALF_UP); 01083 } 01084 s->value = gnc_numeric_convert(value, 01085 get_currency_denom(s), 01086 GNC_HOW_RND_ROUND_HALF_UP); 01087 } 01088 else if (gnc_commodity_equiv(commodity, base_currency)) 01089 { 01090 s->amount = gnc_numeric_convert(value, get_commodity_denom(s), 01091 GNC_HOW_RND_ROUND_HALF_UP); 01092 } 01093 else 01094 { 01095 PERR ("inappropriate base currency %s " 01096 "given split currency=%s and commodity=%s\n", 01097 gnc_commodity_get_printname(base_currency), 01098 gnc_commodity_get_printname(currency), 01099 gnc_commodity_get_printname(commodity)); 01100 return; 01101 } 01102 01103 SET_GAINS_A_VDIRTY(s); 01104 mark_split (s); 01105 qof_instance_set_dirty(QOF_INSTANCE(s)); 01106 xaccTransCommitEdit(s->parent); 01107 } 01108 01109 gnc_numeric 01110 xaccSplitGetBaseValue (const Split *s, const gnc_commodity * base_currency) 01111 { 01112 if (!s || !s->acc || !s->parent) return gnc_numeric_zero(); 01113 01114 /* be more precise -- the value depends on the currency we want it 01115 * expressed in. */ 01116 if (gnc_commodity_equiv(xaccTransGetCurrency(s->parent), base_currency)) 01117 return xaccSplitGetValue(s); 01118 if (gnc_commodity_equiv(xaccAccountGetCommodity(s->acc), base_currency)) 01119 return xaccSplitGetAmount(s); 01120 01121 PERR ("inappropriate base currency %s " 01122 "given split currency=%s and commodity=%s\n", 01123 gnc_commodity_get_printname(base_currency), 01124 gnc_commodity_get_printname(xaccTransGetCurrency (s->parent)), 01125 gnc_commodity_get_printname(xaccAccountGetCommodity(s->acc))); 01126 return gnc_numeric_zero(); 01127 } 01128 01129 /********************************************************************\ 01130 \********************************************************************/ 01131 01132 gnc_numeric 01133 xaccSplitConvertAmount (const Split *split, const Account * account) 01134 { 01135 gnc_commodity *acc_com, *to_commodity; 01136 Transaction *txn; 01137 gnc_numeric amount, value, convrate; 01138 Account * split_acc; 01139 01140 amount = xaccSplitGetAmount (split); 01141 01142 /* If this split is attached to this account, OR */ 01143 split_acc = xaccSplitGetAccount (split); 01144 if (split_acc == account) 01145 return amount; 01146 01147 /* If split->account->commodity == to_commodity, return the amount */ 01148 acc_com = xaccAccountGetCommodity (split_acc); 01149 to_commodity = xaccAccountGetCommodity (account); 01150 if (acc_com && gnc_commodity_equal (acc_com, to_commodity)) 01151 return amount; 01152 01153 /* Ok, this split is not for the viewed account, and the commodity 01154 * does not match. So we need to do some conversion. 01155 * 01156 * First, we can cheat. If this transaction is balanced and has 01157 * exactly two splits, then we can implicitly determine the exchange 01158 * rate and just return the 'other' split amount. 01159 */ 01160 txn = xaccSplitGetParent (split); 01161 if (txn && xaccTransIsBalanced (txn)) 01162 { 01163 const Split *osplit = xaccSplitGetOtherSplit (split); 01164 01165 if (osplit) 01166 { 01167 gnc_commodity* split_comm = 01168 xaccAccountGetCommodity(xaccSplitGetAccount(osplit)); 01169 if (!gnc_commodity_equal(to_commodity, split_comm)) 01170 { 01171 PERR("The split's (%s) amount can't be converted from %s into %s.", 01172 guid_to_string(xaccSplitGetGUID(osplit)), 01173 gnc_commodity_get_mnemonic(split_comm), 01174 gnc_commodity_get_mnemonic(to_commodity) 01175 ); 01176 return gnc_numeric_zero(); 01177 } 01178 return gnc_numeric_neg (xaccSplitGetAmount (osplit)); 01179 } 01180 } 01181 01182 /* ... otherwise, we need to compute the amount from the conversion 01183 * rate into _this account_. So, find the split into this account, 01184 * compute the conversion rate (based on amount/value), and then multiply 01185 * this times the split value. 01186 */ 01187 value = xaccSplitGetValue (split); 01188 01189 if (gnc_numeric_zero_p (value)) 01190 { 01191 return value; 01192 } 01193 01194 convrate = xaccTransGetAccountConvRate(txn, account); 01195 return gnc_numeric_mul (value, convrate, 01196 gnc_commodity_get_fraction (to_commodity), 01197 GNC_HOW_RND_ROUND_HALF_UP); 01198 } 01199 01200 /********************************************************************\ 01201 \********************************************************************/ 01202 01203 gboolean 01204 xaccSplitDestroy (Split *split) 01205 { 01206 Account *acc; 01207 Transaction *trans; 01208 GncEventData ed; 01209 01210 if (!split) return TRUE; 01211 01212 acc = split->acc; 01213 trans = split->parent; 01214 if (acc && !qof_instance_get_destroying(acc) 01215 && xaccTransGetReadOnly(trans)) 01216 return FALSE; 01217 01218 xaccTransBeginEdit(trans); 01219 ed.node = split; 01220 ed.idx = xaccTransGetSplitIndex(trans, split); 01221 qof_instance_set_dirty(QOF_INSTANCE(split)); 01222 qof_instance_set_destroying(split, TRUE); 01223 qof_event_gen(&trans->inst, GNC_EVENT_ITEM_REMOVED, &ed); 01224 xaccTransCommitEdit(trans); 01225 01226 return TRUE; 01227 } 01228 01229 /********************************************************************\ 01230 \********************************************************************/ 01231 01232 gint 01233 xaccSplitOrder (const Split *sa, const Split *sb) 01234 { 01235 int retval; 01236 int comp; 01237 char *da, *db; 01238 01239 if (sa == sb) return 0; 01240 /* nothing is always less than something */ 01241 if (!sa) return -1; 01242 if (!sb) return +1; 01243 01244 retval = xaccTransOrder (sa->parent, sb->parent); 01245 if (retval) return retval; 01246 01247 /* otherwise, sort on memo strings */ 01248 da = sa->memo ? sa->memo : ""; 01249 db = sb->memo ? sb->memo : ""; 01250 retval = g_utf8_collate (da, db); 01251 if (retval) 01252 return retval; 01253 01254 /* otherwise, sort on action strings */ 01255 da = sa->action ? sa->action : ""; 01256 db = sb->action ? sb->action : ""; 01257 retval = g_utf8_collate (da, db); 01258 if (retval != 0) 01259 return retval; 01260 01261 /* the reconciled flag ... */ 01262 if (sa->reconciled < sb->reconciled) return -1; 01263 if (sa->reconciled > sb->reconciled) return +1; 01264 01265 /* compare amounts */ 01266 comp = gnc_numeric_compare(xaccSplitGetAmount(sa), xaccSplitGetAmount (sb)); 01267 if (comp < 0) return -1; 01268 if (comp > 0) return +1; 01269 01270 comp = gnc_numeric_compare(xaccSplitGetValue(sa), xaccSplitGetValue (sb)); 01271 if (comp < 0) return -1; 01272 if (comp > 0) return +1; 01273 01274 /* if dates differ, return */ 01275 DATE_CMP(sa, sb, date_reconciled); 01276 01277 /* else, sort on guid - keeps sort stable. */ 01278 retval = qof_instance_guid_compare(sa, sb); 01279 if (retval) return retval; 01280 01281 return 0; 01282 } 01283 01284 gint 01285 xaccSplitOrderDateOnly (const Split *sa, const Split *sb) 01286 { 01287 Transaction *ta, *tb; 01288 01289 if (sa == sb) return 0; 01290 /* nothing is always less than something */ 01291 if (!sa) return -1; 01292 if (!sb) return +1; 01293 01294 ta = sa->parent; 01295 tb = sb->parent; 01296 if ( !ta && !tb ) return 0; 01297 if ( !tb ) return -1; 01298 if ( !ta ) return +1; 01299 01300 /* if dates differ, return */ 01301 DATE_CMP(ta, tb, date_posted); 01302 01303 /* If the dates are the same, do not change the order */ 01304 return -1; 01305 } 01306 01307 static gboolean 01308 get_corr_account_split(const Split *sa, const Split **retval) 01309 { 01310 01311 const Split *current_split; 01312 GList *node; 01313 gnc_numeric sa_value, current_value; 01314 gboolean sa_value_positive, current_value_positive, seen_one = FALSE; 01315 01316 *retval = NULL; 01317 g_return_val_if_fail(sa, FALSE); 01318 01319 sa_value = xaccSplitGetValue (sa); 01320 sa_value_positive = gnc_numeric_positive_p(sa_value); 01321 01322 for (node = sa->parent->splits; node; node = node->next) 01323 { 01324 current_split = node->data; 01325 if (current_split == sa) continue; 01326 01327 if (!xaccTransStillHasSplit(sa->parent, current_split)) continue; 01328 current_value = xaccSplitGetValue (current_split); 01329 current_value_positive = gnc_numeric_positive_p(current_value); 01330 if ((sa_value_positive && !current_value_positive) || 01331 (!sa_value_positive && current_value_positive)) 01332 { 01333 if (seen_one) 01334 { 01335 *retval = NULL; 01336 return FALSE; 01337 } 01338 else 01339 { 01340 *retval = current_split; 01341 seen_one = TRUE; 01342 } 01343 } 01344 } 01345 return seen_one; 01346 } 01347 01348 /* TODO: these static consts can be shared. */ 01349 const char * 01350 xaccSplitGetCorrAccountName(const Split *sa) 01351 { 01352 static const char *split_const = NULL; 01353 const Split *other_split; 01354 01355 if (!get_corr_account_split(sa, &other_split)) 01356 { 01357 if (!split_const) 01358 split_const = _("-- Split Transaction --"); 01359 01360 return split_const; 01361 } 01362 01363 return xaccAccountGetName(other_split->acc); 01364 } 01365 01366 char * 01367 xaccSplitGetCorrAccountFullName(const Split *sa) 01368 { 01369 static const char *split_const = NULL; 01370 const Split *other_split; 01371 01372 if (!get_corr_account_split(sa, &other_split)) 01373 { 01374 if (!split_const) 01375 split_const = _("-- Split Transaction --"); 01376 01377 return g_strdup(split_const); 01378 } 01379 return gnc_account_get_full_name(other_split->acc); 01380 } 01381 01382 const char * 01383 xaccSplitGetCorrAccountCode(const Split *sa) 01384 { 01385 static const char *split_const = NULL; 01386 const Split *other_split; 01387 01388 if (!get_corr_account_split(sa, &other_split)) 01389 { 01390 if (!split_const) 01391 /* Translators: This string has a disambiguation prefix */ 01392 split_const = Q_("Displayed account code of the other account in a multi-split transaction|Split"); 01393 01394 return split_const; 01395 } 01396 return xaccAccountGetCode(other_split->acc); 01397 } 01398 01399 /* TODO: It's not too hard to make this function avoid the malloc/free. */ 01400 int 01401 xaccSplitCompareAccountFullNames(const Split *sa, const Split *sb) 01402 { 01403 Account *aa, *ab; 01404 char *full_a, *full_b; 01405 int retval; 01406 if (!sa && !sb) return 0; 01407 if (!sa) return -1; 01408 if (!sb) return 1; 01409 01410 aa = sa->acc; 01411 ab = sb->acc; 01412 full_a = gnc_account_get_full_name(aa); 01413 full_b = gnc_account_get_full_name(ab); 01414 retval = g_utf8_collate(full_a, full_b); 01415 g_free(full_a); 01416 g_free(full_b); 01417 return retval; 01418 } 01419 01420 01421 int 01422 xaccSplitCompareAccountCodes(const Split *sa, const Split *sb) 01423 { 01424 Account *aa, *ab; 01425 if (!sa && !sb) return 0; 01426 if (!sa) return -1; 01427 if (!sb) return 1; 01428 01429 aa = sa->acc; 01430 ab = sb->acc; 01431 01432 return safe_strcmp(xaccAccountGetCode(aa), xaccAccountGetCode(ab)); 01433 } 01434 01435 int 01436 xaccSplitCompareOtherAccountFullNames(const Split *sa, const Split *sb) 01437 { 01438 char *ca, *cb; 01439 int retval; 01440 if (!sa && !sb) return 0; 01441 if (!sa) return -1; 01442 if (!sb) return 1; 01443 01444 /* doesn't matter what separator we use 01445 * as long as they are the same 01446 */ 01447 01448 ca = xaccSplitGetCorrAccountFullName(sa); 01449 cb = xaccSplitGetCorrAccountFullName(sb); 01450 retval = safe_strcmp(ca, cb); 01451 g_free(ca); 01452 g_free(cb); 01453 return retval; 01454 } 01455 01456 int 01457 xaccSplitCompareOtherAccountCodes(const Split *sa, const Split *sb) 01458 { 01459 const char *ca, *cb; 01460 if (!sa && !sb) return 0; 01461 if (!sa) return -1; 01462 if (!sb) return 1; 01463 01464 ca = xaccSplitGetCorrAccountCode(sa); 01465 cb = xaccSplitGetCorrAccountCode(sb); 01466 return safe_strcmp(ca, cb); 01467 } 01468 01469 static void 01470 qofSplitSetMemo (Split *split, const char* memo) 01471 { 01472 g_return_if_fail(split); 01473 CACHE_REPLACE(split->memo, memo); 01474 } 01475 01476 void 01477 xaccSplitSetMemo (Split *split, const char *memo) 01478 { 01479 if (!split || !memo) return; 01480 xaccTransBeginEdit (split->parent); 01481 01482 CACHE_REPLACE(split->memo, memo); 01483 qof_instance_set_dirty(QOF_INSTANCE(split)); 01484 xaccTransCommitEdit(split->parent); 01485 01486 } 01487 01488 static void 01489 qofSplitSetAction (Split *split, const char *actn) 01490 { 01491 g_return_if_fail(split); 01492 CACHE_REPLACE(split->action, actn); 01493 } 01494 01495 void 01496 xaccSplitSetAction (Split *split, const char *actn) 01497 { 01498 if (!split || !actn) return; 01499 xaccTransBeginEdit (split->parent); 01500 01501 CACHE_REPLACE(split->action, actn); 01502 qof_instance_set_dirty(QOF_INSTANCE(split)); 01503 xaccTransCommitEdit(split->parent); 01504 01505 } 01506 01507 static void 01508 qofSplitSetReconcile (Split *split, char recn) 01509 { 01510 g_return_if_fail(split); 01511 switch (recn) 01512 { 01513 case NREC: 01514 case CREC: 01515 case YREC: 01516 case FREC: 01517 case VREC: 01518 split->reconciled = recn; 01519 mark_split (split); 01520 xaccAccountRecomputeBalance (split->acc); 01521 break; 01522 default: 01523 PERR("Bad reconciled flag"); 01524 break; 01525 } 01526 } 01527 01528 void 01529 xaccSplitSetReconcile (Split *split, char recn) 01530 { 01531 if (!split || split->reconciled == recn) return; 01532 xaccTransBeginEdit (split->parent); 01533 01534 switch (recn) 01535 { 01536 case NREC: 01537 case CREC: 01538 case YREC: 01539 case FREC: 01540 case VREC: 01541 split->reconciled = recn; 01542 mark_split (split); 01543 qof_instance_set_dirty(QOF_INSTANCE(split)); 01544 xaccAccountRecomputeBalance (split->acc); 01545 break; 01546 default: 01547 PERR("Bad reconciled flag"); 01548 break; 01549 } 01550 xaccTransCommitEdit(split->parent); 01551 01552 } 01553 01554 void 01555 xaccSplitSetDateReconciledSecs (Split *split, time_t secs) 01556 { 01557 if (!split) return; 01558 xaccTransBeginEdit (split->parent); 01559 01560 split->date_reconciled.tv_sec = secs; 01561 split->date_reconciled.tv_nsec = 0; 01562 qof_instance_set_dirty(QOF_INSTANCE(split)); 01563 xaccTransCommitEdit(split->parent); 01564 01565 } 01566 01567 void 01568 xaccSplitSetDateReconciledTS (Split *split, Timespec *ts) 01569 { 01570 if (!split || !ts) return; 01571 xaccTransBeginEdit (split->parent); 01572 01573 split->date_reconciled = *ts; 01574 qof_instance_set_dirty(QOF_INSTANCE(split)); 01575 xaccTransCommitEdit(split->parent); 01576 01577 } 01578 01579 void 01580 xaccSplitGetDateReconciledTS (const Split * split, Timespec *ts) 01581 { 01582 if (!split || !ts) return; 01583 *ts = (split->date_reconciled); 01584 } 01585 01586 Timespec 01587 xaccSplitRetDateReconciledTS (const Split * split) 01588 { 01589 Timespec ts = {0, 0}; 01590 return split ? split->date_reconciled : ts; 01591 } 01592 01593 /********************************************************************\ 01594 \********************************************************************/ 01595 01596 /* return the parent transaction of the split */ 01597 Transaction * 01598 xaccSplitGetParent (const Split *split) 01599 { 01600 return split ? split->parent : NULL; 01601 } 01602 01603 void 01604 xaccSplitSetParent(Split *s, Transaction *t) 01605 { 01606 Transaction *old_trans; 01607 GncEventData ed; 01608 01609 g_return_if_fail(s); 01610 if (s->parent == t) return; 01611 01612 if (s->parent != s->orig_parent && s->orig_parent != t) 01613 PERR("You may not add the split to more than one transaction" 01614 " during the BeginEdit/CommitEdit block."); 01615 xaccTransBeginEdit(t); 01616 old_trans = s->parent; 01617 01618 xaccTransBeginEdit(old_trans); 01619 01620 ed.node = s; 01621 if (old_trans) 01622 { 01623 ed.idx = xaccTransGetSplitIndex(old_trans, s); 01624 qof_event_gen(&old_trans->inst, GNC_EVENT_ITEM_REMOVED, &ed); 01625 } 01626 s->parent = t; 01627 01628 xaccTransCommitEdit(old_trans); 01629 qof_instance_set_dirty(QOF_INSTANCE(s)); 01630 01631 if (t) 01632 { 01633 /* Convert split to new transaction's commodity denominator */ 01634 xaccSplitSetValue(s, xaccSplitGetValue(s)); 01635 01636 /* add ourselves to the new transaction's list of pending splits. */ 01637 if (NULL == g_list_find(t->splits, s)) 01638 t->splits = g_list_append(t->splits, s); 01639 01640 ed.idx = -1; /* unused */ 01641 qof_event_gen(&t->inst, GNC_EVENT_ITEM_ADDED, &ed); 01642 } 01643 xaccTransCommitEdit(t); 01644 } 01645 01646 01647 GNCLot * 01648 xaccSplitGetLot (const Split *split) 01649 { 01650 return split ? split->lot : NULL; 01651 } 01652 01653 void 01654 xaccSplitSetLot(Split* split, GNCLot* lot) 01655 { 01656 xaccTransBeginEdit (split->parent); 01657 split->lot = lot; 01658 qof_instance_set_dirty(QOF_INSTANCE(split)); 01659 xaccTransCommitEdit(split->parent); 01660 } 01661 01662 const char * 01663 xaccSplitGetMemo (const Split *split) 01664 { 01665 return split ? split->memo : NULL; 01666 } 01667 01668 const char * 01669 xaccSplitGetAction (const Split *split) 01670 { 01671 return split ? split->action : NULL; 01672 } 01673 01674 char 01675 xaccSplitGetReconcile (const Split *split) 01676 { 01677 return split ? split->reconciled : ' '; 01678 } 01679 01680 01681 gnc_numeric 01682 xaccSplitGetAmount (const Split * split) 01683 { 01684 return split ? split->amount : gnc_numeric_zero(); 01685 } 01686 01687 gnc_numeric 01688 xaccSplitGetValue (const Split * split) 01689 { 01690 return split ? split->value : gnc_numeric_zero(); 01691 } 01692 01693 gnc_numeric 01694 xaccSplitGetSharePrice (const Split * split) 01695 { 01696 gnc_numeric amt, val, price; 01697 if (!split) return gnc_numeric_create(1, 1); 01698 01699 01700 /* if amount == 0 and value == 0, then return 1. 01701 * if amount == 0 and value != 0 then return 0. 01702 * otherwise return value/amount 01703 */ 01704 01705 amt = xaccSplitGetAmount(split); 01706 val = xaccSplitGetValue(split); 01707 if (gnc_numeric_zero_p(amt)) 01708 { 01709 if (gnc_numeric_zero_p(val)) 01710 return gnc_numeric_create(1, 1); 01711 return gnc_numeric_create(0, 1); 01712 } 01713 price = gnc_numeric_div(val, amt, 01714 GNC_DENOM_AUTO, 01715 GNC_HOW_DENOM_SIGFIGS(PRICE_SIGFIGS) | 01716 GNC_HOW_RND_ROUND_HALF_UP); 01717 01718 /* During random checks we can get some very weird prices. Let's 01719 * handle some overflow and other error conditions by returning 01720 * zero. But still print an error to let us know it happened. 01721 */ 01722 if (gnc_numeric_check(price)) 01723 { 01724 PERR("Computing share price failed (%d): [ %" G_GINT64_FORMAT " / %" 01725 G_GINT64_FORMAT " ] / [ %" G_GINT64_FORMAT " / %" G_GINT64_FORMAT " ]", 01726 gnc_numeric_check(price), val.num, val.denom, amt.num, amt.denom); 01727 return gnc_numeric_create(0, 1); 01728 } 01729 01730 return price; 01731 } 01732 01733 /********************************************************************\ 01734 \********************************************************************/ 01735 01736 QofBook * 01737 xaccSplitGetBook (const Split *split) 01738 { 01739 return qof_instance_get_book(QOF_INSTANCE(split)); 01740 } 01741 01742 const char * 01743 xaccSplitGetType(const Split *s) 01744 { 01745 const char *split_type; 01746 01747 if (!s) return NULL; 01748 split_type = kvp_frame_get_string(s->inst.kvp_data, "split-type"); 01749 return split_type ? split_type : "normal"; 01750 } 01751 01752 /* reconfigure a split to be a stock split - after this, you shouldn't 01753 mess with the value, just the amount. */ 01754 void 01755 xaccSplitMakeStockSplit(Split *s) 01756 { 01757 xaccTransBeginEdit (s->parent); 01758 01759 s->value = gnc_numeric_zero(); 01760 kvp_frame_set_str(s->inst.kvp_data, "split-type", "stock-split"); 01761 SET_GAINS_VDIRTY(s); 01762 mark_split(s); 01763 qof_instance_set_dirty(QOF_INSTANCE(s)); 01764 xaccTransCommitEdit(s->parent); 01765 } 01766 01767 01768 /********************************************************************\ 01769 \********************************************************************/ 01770 /* In the old world, the 'other split' was the other split of a 01771 * transaction that contained only two splits. In the new world, 01772 * a split may have been cut up between multiple lots, although 01773 * in a conceptual sense, if lots hadn't been used, there would be 01774 * only a pair. So we handle this conceptual case: we can still 01775 * identify, unambiguously, the 'other' split when 'this' split 01776 * as been cut up across lots. We do this by looking for the 01777 * 'lot-split' keyword, which occurs only in cut-up splits. 01778 */ 01779 01780 Split * 01781 xaccSplitGetOtherSplit (const Split *split) 01782 { 01783 int i; 01784 Transaction *trans; 01785 int count, num_splits; 01786 Split *other = NULL; 01787 KvpValue *sva; 01788 gboolean trading_accts; 01789 01790 if (!split) return NULL; 01791 trans = split->parent; 01792 if (!trans) return NULL; 01793 01794 #ifdef OLD_ALGO_HAS_ONLY_TWO_SPLITS 01795 Split *s1, *s2; 01796 if (g_list_length (trans->splits) != 2) return NULL; 01797 01798 s1 = g_list_nth_data (trans->splits, 0); 01799 s2 = g_list_nth_data (trans->splits, 1); 01800 01801 if (s1 == split) return s2; 01802 return s1; 01803 #endif 01804 01805 trading_accts = xaccTransUseTradingAccounts (trans); 01806 num_splits = xaccTransCountSplits(trans); 01807 count = num_splits; 01808 sva = kvp_frame_get_slot (split->inst.kvp_data, "lot-split"); 01809 if (!sva && !trading_accts && (2 != count)) return NULL; 01810 01811 for (i = 0; i < num_splits; i++) 01812 { 01813 Split *s = xaccTransGetSplit(trans, i); 01814 if (s == split) 01815 { 01816 --count; 01817 continue; 01818 } 01819 if (kvp_frame_get_slot (s->inst.kvp_data, "lot-split")) 01820 { 01821 --count; 01822 continue; 01823 } 01824 if (trading_accts && 01825 xaccAccountGetType(xaccSplitGetAccount(s)) == ACCT_TYPE_TRADING) 01826 { 01827 --count; 01828 continue; 01829 } 01830 other = s; 01831 } 01832 return (1 == count) ? other : NULL; 01833 } 01834 01835 /********************************************************************\ 01836 \********************************************************************/ 01837 01838 gnc_numeric 01839 xaccSplitVoidFormerAmount(const Split *split) 01840 { 01841 g_return_val_if_fail(split, gnc_numeric_zero()); 01842 return kvp_frame_get_numeric(split->inst.kvp_data, void_former_amt_str); 01843 } 01844 01845 gnc_numeric 01846 xaccSplitVoidFormerValue(const Split *split) 01847 { 01848 g_return_val_if_fail(split, gnc_numeric_zero()); 01849 return kvp_frame_get_numeric(split->inst.kvp_data, void_former_val_str); 01850 } 01851 01852 void 01853 xaccSplitVoid(Split *split) 01854 { 01855 gnc_numeric zero = gnc_numeric_zero(); 01856 KvpFrame *frame = split->inst.kvp_data; 01857 01858 kvp_frame_set_gnc_numeric(frame, void_former_amt_str, 01859 xaccSplitGetAmount(split)); 01860 kvp_frame_set_gnc_numeric(frame, void_former_val_str, 01861 xaccSplitGetValue(split)); 01862 01863 xaccSplitSetAmount (split, zero); 01864 xaccSplitSetValue (split, zero); 01865 xaccSplitSetReconcile(split, VREC); 01866 01867 } 01868 01869 void 01870 xaccSplitUnvoid(Split *split) 01871 { 01872 KvpFrame *frame = split->inst.kvp_data; 01873 01874 xaccSplitSetAmount (split, xaccSplitVoidFormerAmount(split)); 01875 xaccSplitSetValue (split, xaccSplitVoidFormerValue(split)); 01876 xaccSplitSetReconcile(split, NREC); 01877 kvp_frame_set_slot(frame, void_former_amt_str, NULL); 01878 kvp_frame_set_slot(frame, void_former_val_str, NULL); 01879 } 01880 01881 /********************************************************************\ 01882 \********************************************************************/ 01883 /* QofObject function implementation */ 01884 01885 /* Hook into the QofObject registry */ 01886 01887 #ifdef _MSC_VER 01888 /* MSVC compiler doesn't have C99 "designated initializers" 01889 * so we wrap them in a macro that is empty on MSVC. */ 01890 # define DI(x) /* */ 01891 #else 01892 # define DI(x) x 01893 #endif 01894 static QofObject split_object_def = 01895 { 01896 DI(.interface_version = ) QOF_OBJECT_VERSION, 01897 DI(.e_type = ) GNC_ID_SPLIT, 01898 DI(.type_label = ) "Split", 01899 DI(.create = ) (gpointer)xaccMallocSplit, 01900 DI(.book_begin = ) NULL, 01901 DI(.book_end = ) NULL, 01902 DI(.is_dirty = ) qof_collection_is_dirty, 01903 DI(.mark_clean = ) qof_collection_mark_clean, 01904 DI(.foreach = ) qof_collection_foreach, 01905 DI(.printable = ) (const char * (*)(gpointer)) xaccSplitGetMemo, 01906 DI(.version_cmp = ) (int (*)(gpointer, gpointer)) qof_instance_version_cmp, 01907 }; 01908 01909 static gpointer 01910 split_account_guid_getter (gpointer obj, const QofParam *p) 01911 { 01912 Split *s = obj; 01913 Account *acc; 01914 01915 if (!s) return NULL; 01916 acc = xaccSplitGetAccount (s); 01917 if (!acc) return NULL; 01918 return ((gpointer)xaccAccountGetGUID (acc)); 01919 } 01920 01921 static double /* internal use only */ 01922 DxaccSplitGetShareAmount (const Split * split) 01923 { 01924 return split ? gnc_numeric_to_double(xaccSplitGetAmount(split)) : 0.0; 01925 } 01926 01927 static gpointer 01928 no_op (gpointer obj, const QofParam *p) 01929 { 01930 return obj; 01931 } 01932 01933 static void 01934 qofSplitSetParentTrans(Split *s, QofInstance *ent) 01935 { 01936 Transaction *trans = (Transaction*)ent; 01937 01938 g_return_if_fail(trans); 01939 xaccSplitSetParent(s, trans); 01940 } 01941 01942 static void 01943 qofSplitSetAccount(Split *s, QofInstance *ent) 01944 { 01945 Account *acc = (Account*)ent; 01946 01947 g_return_if_fail(acc); 01948 xaccSplitSetAccount(s, acc); 01949 } 01950 01951 gboolean xaccSplitRegister (void) 01952 { 01953 static const QofParam params[] = 01954 { 01955 { 01956 SPLIT_DATE_RECONCILED, QOF_TYPE_DATE, 01957 (QofAccessFunc)xaccSplitRetDateReconciledTS, 01958 (QofSetterFunc)xaccSplitSetDateReconciledTS 01959 }, 01960 01961 /* d-* are deprecated query params, should not be used in new 01962 * queries, should be removed from old queries. */ 01963 { 01964 "d-share-amount", QOF_TYPE_DOUBLE, 01965 (QofAccessFunc)DxaccSplitGetShareAmount, NULL 01966 }, 01967 { 01968 "d-share-int64", QOF_TYPE_INT64, 01969 (QofAccessFunc)qof_entity_get_guid, NULL 01970 }, 01971 { 01972 SPLIT_BALANCE, QOF_TYPE_NUMERIC, 01973 (QofAccessFunc)xaccSplitGetBalance, NULL 01974 }, 01975 { 01976 SPLIT_CLEARED_BALANCE, QOF_TYPE_NUMERIC, 01977 (QofAccessFunc)xaccSplitGetClearedBalance, NULL 01978 }, 01979 { 01980 SPLIT_RECONCILED_BALANCE, QOF_TYPE_NUMERIC, 01981 (QofAccessFunc)xaccSplitGetReconciledBalance, NULL 01982 }, 01983 { 01984 SPLIT_MEMO, QOF_TYPE_STRING, 01985 (QofAccessFunc)xaccSplitGetMemo, (QofSetterFunc)qofSplitSetMemo 01986 }, 01987 { 01988 SPLIT_ACTION, QOF_TYPE_STRING, 01989 (QofAccessFunc)xaccSplitGetAction, (QofSetterFunc)qofSplitSetAction 01990 }, 01991 { 01992 SPLIT_RECONCILE, QOF_TYPE_CHAR, 01993 (QofAccessFunc)xaccSplitGetReconcile, 01994 (QofSetterFunc)qofSplitSetReconcile 01995 }, 01996 { 01997 SPLIT_AMOUNT, QOF_TYPE_NUMERIC, 01998 (QofAccessFunc)xaccSplitGetAmount, (QofSetterFunc)qofSplitSetAmount 01999 }, 02000 { 02001 SPLIT_SHARE_PRICE, QOF_TYPE_NUMERIC, 02002 (QofAccessFunc)xaccSplitGetSharePrice, 02003 (QofSetterFunc)qofSplitSetSharePrice 02004 }, 02005 { 02006 SPLIT_VALUE, QOF_TYPE_DEBCRED, 02007 (QofAccessFunc)xaccSplitGetValue, (QofSetterFunc)qofSplitSetValue 02008 }, 02009 { SPLIT_TYPE, QOF_TYPE_STRING, (QofAccessFunc)xaccSplitGetType, NULL }, 02010 { 02011 SPLIT_VOIDED_AMOUNT, QOF_TYPE_NUMERIC, 02012 (QofAccessFunc)xaccSplitVoidFormerAmount, NULL 02013 }, 02014 { 02015 SPLIT_VOIDED_VALUE, QOF_TYPE_NUMERIC, 02016 (QofAccessFunc)xaccSplitVoidFormerValue, NULL 02017 }, 02018 { SPLIT_LOT, GNC_ID_LOT, (QofAccessFunc)xaccSplitGetLot, NULL }, 02019 { 02020 SPLIT_TRANS, GNC_ID_TRANS, 02021 (QofAccessFunc)xaccSplitGetParent, 02022 (QofSetterFunc)qofSplitSetParentTrans 02023 }, 02024 { 02025 SPLIT_ACCOUNT, GNC_ID_ACCOUNT, 02026 (QofAccessFunc)xaccSplitGetAccount, (QofSetterFunc)qofSplitSetAccount 02027 }, 02028 { SPLIT_ACCOUNT_GUID, QOF_TYPE_GUID, split_account_guid_getter, NULL }, 02029 /* these are no-ops to register the parameter names (for sorting) but 02030 they return an allocated object which getters cannot do. */ 02031 { SPLIT_ACCT_FULLNAME, SPLIT_ACCT_FULLNAME, no_op, NULL }, 02032 { SPLIT_CORR_ACCT_NAME, SPLIT_CORR_ACCT_NAME, no_op, NULL }, 02033 { SPLIT_CORR_ACCT_CODE, SPLIT_CORR_ACCT_CODE, no_op, NULL }, 02034 { SPLIT_KVP, QOF_TYPE_KVP, (QofAccessFunc)xaccSplitGetSlots, NULL }, 02035 { QOF_PARAM_BOOK, QOF_ID_BOOK, (QofAccessFunc)xaccSplitGetBook, NULL }, 02036 { 02037 QOF_PARAM_GUID, QOF_TYPE_GUID, 02038 (QofAccessFunc)qof_entity_get_guid, NULL 02039 }, 02040 { NULL }, 02041 }; 02042 02043 qof_class_register (GNC_ID_SPLIT, (QofSortFunc)xaccSplitOrder, params); 02044 qof_class_register (SPLIT_ACCT_FULLNAME, 02045 (QofSortFunc)xaccSplitCompareAccountFullNames, NULL); 02046 qof_class_register (SPLIT_CORR_ACCT_NAME, 02047 (QofSortFunc)xaccSplitCompareOtherAccountFullNames, 02048 NULL); 02049 qof_class_register (SPLIT_CORR_ACCT_CODE, 02050 (QofSortFunc)xaccSplitCompareOtherAccountCodes, NULL); 02051 02052 return qof_object_register (&split_object_def); 02053 } 02054 02055 SplitTestFunctions* 02056 _utest_split_fill_functions (void) 02057 { 02058 SplitTestFunctions *func = g_new (SplitTestFunctions, 1); 02059 02060 func->xaccSplitEqualCheckBal = xaccSplitEqualCheckBal; 02061 func->get_currency_denom = get_currency_denom; 02062 func->get_commodity_denom = get_commodity_denom; 02063 func->get_corr_account_split = get_corr_account_split; 02064 return func; 02065 } 02066 02067 /************************ END OF ************************************\ 02068 \************************* FILE *************************************/
1.7.4